#coding=utf-8
import math
import time
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from measurement.preprocess_probe_data import *
from pylab import mpl
from utils.mylib_db import *
from utils.mylib_utils import *
from utils.mcm_algorithm.MCM import *
from utils.gum_algorithm23.GUM import *
from utils.gum_algorithm23.parse_data import *
from utils.gum_algorithm23.preprocess_data import *
from utils.gum_algorithm23.main_GUM import *
from utils.gum_algorithm23.main_GUM_balloon import *
from utils.gum_algorithm23.main_GUM_rocket import *
from utils.gum_algorithm23.main_MCM import *
from utils.gum_algorithm23.test_main import *
from measurement.measurement_utils import *
from measurement.open_dir_and_file import *
from measurement.preprocess_probe_data import *
import multiprocessing


"""
评定不确定度算法：
GUM:
MCM:

"""
LIGHTSPEED = 3*10**8
EPSINON = 1e-6          #用于浮点数比较的差值
DEFAULTTIME = "0001-01-01 00:00:00"   # 兼容人大金仓数据库

db_data_head_table = "bt_data_head"
db_raw_l0_data_table = "bt_data_l0_raw"
db_raw_l2_data_table = "bt_data_l2_raw"
db_raw_l0_precessed_table = "bt_data_l0"
db_raw_l2_precessed_table = "bt_data"
db_temporary_data_table_l2 = "bt_temporary_data"

# MCM参数列表
deviceLidar02SystemTypeInfoForMcm = {}

def performUncertainty(param):
    """
    不确定度评定
    """
    print("进入不确定度评定过程..")
    print("参数情况：")
    print(param)

    #参数处理
    task_id = param.get('task_id') if param.get('task_id') != None else get_current_timestamp()                         # TaskId默认为当前时间

    # 筛选条件（数据查询参数）
    data_source = int(param.get('data_source')) if param.get('data_source') is not None else 0                          # 数据来源（枚举值）：0:数据库预处理数据，1:数据库原始数据 2:文件导入;3:最新处理的数据
    station_device_pair_list = param.get('station_device_pair_list') if param.get('station_device_pair_list') is not None else ""       # 站点设备对字符串，格式"1-2", 或者 "1-2,1-3,2-4" (某些模块传多组参数)
    station_id = int(param.get('station_id')) if param.get('station_id') is not None else 0                             # 站点编号
    device_id = int(param.get('device_id')) if param.get('device_id') is not None else 0                                # 设备编号
    # value_type_str = param.get('value_type') if param.get('value_type') is not None else ""                           # 参量类型，格式："0" 或 "0,1,2,3"，其中0:温度；1:密度；2:纬向风速; 3:径向风速；（2309调整：改成内部转换，不接受界面输入）
    data_type = param.get('data_type') if param.get('data_type') is not None else ""                                    # 数据类型，格式"TSVP" 或 "TSVP, DUSM"等（2309调整为：T/P/W1/W2/D/N/F）
    time_start = param.get('time_start') if param.get('time_start') is not None else ""                                 # 时间范围的起始时间，格式：YYYY-mm-dd HH:MM:SS
    time_end = param.get('time_end') if param.get('time_end') is not None else ""                                       # 时间范围的结束时间，格式：YYYY-mm-dd HH:MM:SS
    height_start = param.get('height_start') if param.get('height_start') is not None else ""                           # 高度区间起始值（暂时不使用）
    height_end = param.get('height_end') if param.get('height_end') is not None else ""                                 # 高度区间结束值（暂时不使用）

    # 文件导入路径（data_source=2时生效）
    import_file_path = param.get('import_file_path') if param.get('import_file_path') is not None else ""               # 文件导入支持参数：单个文件路径，多个文件路径，单个目录，格式"path1" 或 "path1;path2;path3"或"directory"（当data_source选择了2文件导入后，该字段必填）
    process_num = int(param.get('process_num')) if param.get('process_num') is not None else 1                          # 默认1个进程进行评定算法
    user_id = int(param.get('user_id')) if param.get('user_id') is not None else -1                                     # 当前登录用户

    # 指定数据文件
    head_id_list = param.get('head_id_list') if param.get('head_id_list') is not None else ""                           # 默认值为：""，格式为 2,3,4,5
    data_level = int(param.get('data_level')) if param.get('data_level') is not None else 1                             # 默认为1，即L2数据

    # 算法参数
    measure_method = int(param.get('measure_method')) if param.get('measure_method') is not None else -1                # 评定算法（枚举值）：0:GUM,1:MCM  --必填
    factor = param.get('factor') if param.get('factor') is not None else ""                                             # 包含因子K（整数或小数）  --选择了GUM算法后，必填
    mcm_parameter_p = param.get('mcm_parameter_p') if param.get('mcm_parameter_p') is not None else ""                  # MCM参数：置信概率p，值如99.73%、99%、95%等   --选择了MCM算法后，必填
    mcm_exp_times = param.get('mcm_exp_times') if param.get('mcm_exp_times') is not None else ""                        # MCM实验次数，（非必填，从参数mcm_parameter_p可计算得来）
    save_flag = int(param.get('save_flag')) if param.get('save_flag') is not None else 1                                # 预处理结果存入数据库（完成）（默认存库）

    if measure_method == -1:
        print("请选择评定算法！")
        return {"code": -1, "msg": "请选择评定算法！"}
    elif measure_method == 0:
        print("判断GUM算法参数")
        print("test-factor: %s" % factor)
        if factor == "":
            return {"code": -1, "msg": "参数值错误：GUM算法参数K不能为空！"}
        if (str_to_float(factor) - 3.0) > EPSINON or (str_to_float(factor) - 0.99999) < EPSINON:
            return {"code": -1, "msg": "参数值错误：GUM算法参数K值范围应 大于等于1，小于等于3！"}
    elif measure_method == 1:
        print("判断MCM算法参数")
        print("test-mcm_parameter_p: %s" % mcm_parameter_p)
        if mcm_parameter_p == "":
            return {"code": -1, "msg": "参数值错误：MCM算法参数p不能为空！"}
        if (str_to_float(mcm_parameter_p) - 0.9973) > EPSINON or (str_to_float(mcm_parameter_p) - 0.0) <= EPSINON:
            return {"code": -1, "msg": "参数值错误：MCM算法参数置信概率p应大于0，小于等于0.9973！"}

    # 新老探测体制临时转换（2023-9修改）
    print("data_type:", data_type)
    # 临时注释，需打开
    # data_type = getMultiDataTypeStr2309(data_type)
    value_type_str = getMultiValutTypeStrByDataType2309(data_type)
    print("data_type2:", data_type)
    print("value_type2:", value_type_str)

    conf = {
        'TaskId': task_id,
        # 任务种类：0:数据录入；1:数据预处理；2:不确定度评定;3:数据分析数据处理;
        # 5:一键评定;6:评定结果查询（文件上传）；7:不确定度校验（文件上传）；8:数据比对不同设备；9:数据比对不同时间
        "TaskType": 2,
        'SaveFlag': save_flag,
        'DataLevel': data_level,
        'MeasurementMethod': measure_method,
        'Factor': str_to_float(factor),
        'McmParameterP': mcm_parameter_p,
        # 'McmParameterExpTimes': computeExperimentTimes(mcm_parameter_p) if mcm_exp_times == "" else str_to_float(mcm_exp_times),
        'McmParameterExpTimes': 0,
        # 数据源
        'DataSource': data_source,
        # 确定数据来源系统对应的表
        'HeadTableType': "",      # 通过设备名称获取系统类型（系统1-系统4）不同（在接口queryDataByCondition2309中确定具体内容）
        # 支持指定headIdList
        'HeadIdList': head_id_list,

        'StationDevicePairList': station_device_pair_list.split(',') if station_device_pair_list != "" else [],
        'StationId': station_id,
        'DeviceId': device_id,
        'ValueType': value_type_str,
        'DataType': data_type,
        'TimeStart': time_start,
        'TimeEnd': time_end,
        'HeightStart': height_start,
        'HeightEnd': height_end,

        'DataLevel': data_level,

        # 用户文件导入相关
        'ImportFilePath': import_file_path,
        "ProcessNum": process_num,
        "UserId": user_id,

        'DbHeadTable': "",          # 数据筛选源Head表
        'DbTable': "",              # 数据筛选源Data表
        'DbSaveHeadTable': "",      # 数据存储Head表
        'DbSaveTable': "",          # 数据存储Data表
    }

    print("参数测试：")
    print(conf)

    if measure_method == 1:
        conf['McmParameterExpTimes'] = computeExperimentTimes(mcm_parameter_p) if mcm_exp_times == "" else str_to_float(mcm_exp_times)
        print(conf)

    # 不支持对最近一次录入的数据进行预处理！（但是代码里面已经支持该情况，需测试后启用）
    if conf['DataSource'] == 3:
        # print("不支持对最近一次录入的数据进行预处理！（后续可增加此功能）")
        # return {"code": -1, "msg": "不支持对最近一次录入的数据进行预处理！"}
        print("对最近一次录入的数据进行处理..")

    if conf['DataSource'] == 2 and conf['ImportFilePath'] == "":
        return {"code": -1, "msg": "数据源选择了“文件导入”，需指定导入文件或文件夹路径！"}

    # 指定数据筛选库表和存储库表
    # 指定保存数据的库表（预处理数据对象是L2,如果存库，进正式表，如果不存，临时表）
    # 针对其他场景，进行修改（导入文件，存临时表）
    # 数据来源（枚举值）：0:数据库预处理数据，1:数据库原始数据 2:文件导入;3:最新处理的数据
    # 保存数据
    if conf['SaveFlag'] == 1:
        conf['DbSaveHeadTable'] = db_data_head_table
        conf['DbSaveTable'] = db_raw_l2_precessed_table
    else:
        conf['DbSaveHeadTable'] = db_temporary_data_head_table
        conf['DbSaveTable'] = db_temporary_data_table_l2
        # TODO ???
        print("清空临时表数据，准备本次数据保存..")
        clear_db_temporary_table_and_keep_last_same_type_task(2)

    # 获取数据（conf['DataSource'] == 3情况，需根据任务表是否存库字段确定）
    if conf['DataSource'] == 0:
        conf['DbHeadTable'] = db_data_head_table
        conf['DbTable'] = db_raw_l2_precessed_table
    elif conf['DataSource'] == 1:
        conf['DbHeadTable'] = db_data_head_table
        conf['DbTable'] = db_raw_l2_data_table
    elif conf['DataSource'] == 2:
        #  2:文件导入：临时表，结果不存库
        conf['DbHeadTable'] = db_temporary_data_head_table
        conf['DbTable'] = db_temporary_data_table_l2
        conf['DbSaveHeadTable'] = db_temporary_data_head_table
        conf['DbSaveTable'] = db_temporary_data_table_l2
        # TODO ???

        # 清空DataType，根据具体数据来确定
        conf['DataType'] = ""

        print("清空临时表数据，准备本次数据保存..")
        clear_db_temporary_table_and_keep_last_same_type_task(2)
    # elif conf['DataSource'] == 3:  # 查任务表后进行确认数据存储源数据库表
    else:
        conf['DbHeadTable'] = db_temporary_data_head_table
        conf['DbTable'] = db_temporary_data_table_l2
        # 清空DataType，根据具体数据来确定（防止数据源为3时，取之前设置好的界面遗留的参数）
        conf['DataType'] = ""

    # 设置任务开始
    # 设置本批次交互任务的ID
    TASK_ID = conf['TaskId']
    if TASK_ID != "":
        ret = setTaskStatus(TASK_ID, conf['TaskType'], conf['SaveFlag'], -1, conf['DataSource'])
        if ret != "OK":
            print("setTaskStatus: %s" % ret)
            return ret

    # 所有业务逻辑放在子进程中执行
    processes = []
    process = multiprocessing.Process(target=runUncertaintyAnalysisTaskFunc, args=(conf, TASK_ID))  # 正式运行用（捕捉错误，设置任务完成状态）
    # process = multiprocessing.Process(target=runUncertaintyAnalysisTask, args=(conf, TASK_ID))    # 开发调试用，暴露出错误代码行号
    processes.append(process)
    process.start()

    return 0


def runUncertaintyAnalysisTaskFunc(conf, TASK_ID):
    try:
        start_time = time.time()    # 计时开始
        ret = runUncertaintyAnalysisTask(conf, TASK_ID)
        print("运行不确定度程序：", ret)
        end_time = time.time()      # 计时结束
        #计算代码运行时间(ms)
        run_time = (end_time - start_time) * 1000
        #print('代码运行时间为{:.2f} s'.format(run_time))
        print('代码运行时间为%d ms' % run_time)

        # if ret != 0:
        print("设置任务状态为 完成状态 ！")
        setTaskStatusFinish(conf)

        # 以tkinter窗口形式给出系统错误提示
        if 'code' in ret.keys() and ret['code'] == -1:
            err_msg = ret['msg']
            print("错误：", err_msg)
            show_msg_to_tkinter(err_msg)

    except Exception as e:
        print("runUncertaintyAnalysisTaskFunc_Error: %s", e)
        setTaskStatusFinish(conf)
        # 以tkinter窗口形式给出系统错误提示
        show_msg_to_tkinter(e)


def runUncertaintyAnalysisTask(conf, TASK_ID):
    ######################################
    # 保存数据
    if conf['DataSource'] == 2:
        print("开始导入用户数据..")
        # print("先清除同类任务的旧数据..")
        # delete_pre_data_by_batch_id(conf['DbTable'], conf['TaskType'])
        # ret = callReadFileProcessAndWaitFinish(conf, conf['ImportFilePath'], conf['ProcessNum'], TASK_ID)
        conf['FileList'] = [conf['ImportFilePath']]
        # print("从本地文件读入数据：", conf)
        # return {"code": -1, "msg": "测试调试代码..."}
        ret = callReadFileProcessAndWaitFinish2309(conf, TASK_ID)
        print("读取用户文件结束！")
        print("进入筛选文件数据阶段..")

    ######################################
    # 获取数据
    print("开始筛选文件数据阶段..")
    head_id_list = conf['HeadIdList']

    # 按查询条件获取探测数据（head_id_list）
    data_head_id_list = []

    # 查询流程：1）优先检查是否提供了headid，2）其次根据datasource判断是按条件检索或检索最近一次任务的结果
    if head_id_list != "":
        # 如果参数中指定了head_id_list
        print("参数指定了Head_id_list!优先处理..")
        # data_head_id_list = []
        head_id_list_arr = head_id_list.split(",")
        if len(head_id_list_arr) > 0:
            for head_id in head_id_list_arr:
                print(head_id)
                ret = getHeadIdInfoByHeadId(int(head_id), 0)
                if ret != "":
                    data_head_id_list.append(ret)
        else:
            print("参数head_id_list格式错误！")
            return {"code": -1, "msg": "提供的参数head_id_list格式错误！"}

    elif conf['DataSource'] == 0 or conf['DataSource'] == 1:
        # 对数据源 0:数据库预处理数据，1:数据库原始数据 2:文件导入;
        print("根据数据源，按条件筛选head_id_list..")
        if conf['ValueType'] == "":
            print("参量类型ValueType不能为空")
            return {"code": -1, "msg": "当前选择的数据源，参量类型参数不能为空！"}

        # 同时支持传station-device 对和station_id / device_id单独赋值情况
        if len(conf['StationDevicePairList'])>0:
            for pair in conf['StationDevicePairList']:
                arr = pair.split('-')
                conf['StationId'] = int(arr[0])
                conf['DeviceId'] = int(arr[1])
                # data_head_id_list_part = queryDataByCondition(conf, 0)
                data_head_id_list_part = queryDataByCondition2309(conf, 0)  # 针对202309数据分表存储情况查询
                if len(data_head_id_list_part) > 0:
                    data_head_id_list.extend(data_head_id_list_part)
        else:
            # data_head_id_list = queryDataByCondition(conf, 0)
            data_head_id_list = queryDataByCondition2309(conf, 0)   # 针对202309数据分表存储情况查询

        if len(data_head_id_list) == 0:
            print("错误：未检索到探测数据，请重新设置检索条件！")
            return {"code": -1, "msg": "错误：未检索到探测数据，请重新设置检索条件！"}
        print("XXXXXXXX站点-设备参数XXXXXXXX：", data_head_id_list)

    elif conf['DataSource'] == 3:
        # 对数据源 3:最新处理的数据。获取最近一次的录入数据，对此进行数据预处理
        # 根据任务确定数据库表筛选数据情况
        print("取最新一次任务处理结束的数据..")
        data_batch_info = getLatestDataBatchId(1)
        if data_batch_info == "":
            print("错误：未找到最近录入的数据")
            return {"code": -1, "msg": "未找到最近录入的数据！"}
        data_batch_id = data_batch_info['KeyName']
        # 如果字段“KeyNameUpdate”有值，说明该taskId已被后续过程（预处理、评定等）修改
        # 修改后的值为 “KeyNameUpdate”的值
        if data_batch_info['KeyNameUpdate'] is not None and data_batch_info['KeyNameUpdate'] != '':
            data_batch_id = data_batch_info['KeyNameUpdate']

        # 此字段用于设置任务表的KeyNameUpdate字段，便于再次获取最近一次任务的数据
        # （因预处理、评定会更新最近一次的batchId，使用KeyNameUpdate字段进行数据跟踪）
        conf['LatestTaskId'] = data_batch_info['Id']
        conf['PreviousTaskId'] = data_batch_id

        #0:不存库，预处理的数据在临时表，1:存库，预处理的数据在正式预处理后的表
        #input_data_loc = data_batch_info[3]
        print("data_batch_id: %s" % data_batch_id)

        # 确定数据录入的数据存储数据库源表（TODO：校验？）
        if data_batch_info['IsSaveIntoDb'] == 1:
            conf['DbHeadTable'] = db_data_head_table
            conf['DbTable'] = db_raw_l2_precessed_table
        elif data_batch_info['IsSaveIntoDb'] == 0:
            conf['DbHeadTable'] = db_temporary_data_head_table
            conf['DbTable'] = db_temporary_data_table_l2

        # data_head_id_list = getHeadIdByBatchId(data_batch_id, conf['DataLevel'], 0)
        # data_head_id_list = getHeadIdByBatchIdV2(data_batch_id, conf['DataLevel'], 0)
        data_head_id_list = getHeadIdByBatchIdV3(conf, data_batch_id, conf['DataLevel'], 0)
        if len(data_head_id_list) == 0:
            print("错误：未找到最近录入数据的head_id")
            return {"code": -1, "msg": "未找到最近录入数据的head_id！"}

    elif conf['DataSource'] == 2:
        # 对数据源 3:最新处理的数据。获取最近一次的录入数据，对此进行数据预处理
        print("获取刚刚导入的用户文件数据..")
        data_batch_id = TASK_ID
        print("data_batch_id: %s" % data_batch_id)

        data_head_id_list = getHeadIdByBatchIdV3(conf, data_batch_id, conf['DataLevel'], 0)
        if len(data_head_id_list) == 0:
            print("错误：未找到最近录入数据的head_id")
            return {"code": -1, "msg": "未找到最近录入数据的head_id！"}
    else:
        print("参数错误！")
        return {"code": -1, "msg": "参数错误，请检查传入的参数！"}

    print("data_head_id_list: ")
    print(data_head_id_list)
    print("检索到HearID数量：%d" % len(data_head_id_list))
    if len(data_head_id_list) == 0:
        print("未获取到探测数据！")
        return {"code": -1, "msg": "未获取到探测数据"}

    # time.sleep(2)

    # performUncertaintyAlgorithm(data_head_id_list, conf, TASK_ID)

    # 如果MCM算法，从数据库获取设备参数（一期的逻辑，二期不用）
    # if conf['MeasurementMethod'] == 1:
    #     deviceLidar02SystemTypeInfoForMcm = getDeviceParameterBySystemTypeFromBD()
    #     print(deviceLidar02SystemTypeInfoForMcm)
    #     conf['DeviceLidar02SystemTypeInfoForMcm'] = deviceLidar02SystemTypeInfoForMcm

    # 读取所有文件按进程分组
    file_bin = split_file_into_bin(data_head_id_list, conf['ProcessNum'])
    print(file_bin)

    conf['BatchId'] = TASK_ID
    # 数据读取计时开始：
    start_time1 = time.time()
    processes = []
    for key in file_bin.keys():
        print("启动进程：%s" % key)
        # process = multiprocessing.Process(target=performUncertaintyAlgorithm, args=(file_bin[key], conf, TASK_ID))
        process = multiprocessing.Process(target=performUncertaintyAlgorithm2309, args=(file_bin[key], conf))
        processes.append(process)
        process.start()

    # 主进程等待子进程结束（阻塞执行）
    for proc in processes:
        proc.join()

    # 子进程中设置任务结束(在调用函数中已经设置结束状态，这里不需要)
    # print("将要设置评定任务结束..")
    # if TASK_ID != "":
    #     print("设置任务结束！")
    #     updateTaskStatus(TASK_ID, conf['TaskType'])

    # 数据读取计时结束：
    end_time1 = time.time()

    # 计算代码运行时间(ms)
    run_time = (end_time1 - start_time1) * 1000
    print("评定数量：%d" % len(data_head_id_list))
    print("评定时间：")
    print(run_time)

    # return 0
    return {"code": 0, "msg": "不确定度评定完成！"}


def split_file_into_bin(data_head_id_list, process_num):
    """
    按进程对headid分组
    """
    file_bin = {}
    count = 0
    for head_id_info in data_head_id_list:
        key = str(count % process_num)
        if key not in file_bin.keys():
            file_bin[key] = []
        file_bin[key].append(head_id_info)
        count += 1

    print("评定文件总数：%d" % count)
    if count > 0:
        print("探测文件：")
        print(file_bin.keys())
    return file_bin


# 针对202309数据的不确定度评定算法
def performUncertaintyAlgorithm2309(data_head_id_list, conf):
    # 计数本次评定的所有数据条数
    print("数据评定对象：", data_head_id_list)
    print("数据评定对象数量：", len(data_head_id_list))
    # 根据head_id，测量数据类型value_type，获取原始数据
    for (head_id, value_type, start_time, end_time, station_id, device_id, file_name, file_store_path) in data_head_id_list:
        print("head_id = %d, value_type = %s" % (head_id, value_type))

        L2_filepath = os.path.join(file_store_path, file_name)
        L2_filepath = L2_filepath.replace("\\", "/")
        print(L2_filepath)

        # 获取DataType（原位气球数据评定使用）
        data_type = checkL2DataTypeByValueType2309(int(value_type))

        # 判断激光雷达或者原位
        deviceType = checkDeviceTypeByFileName(file_name)

        # 用于查找L2的L0文件（方法0），同时用于不确定度评定结果保存所需信息
        db_table_head_type = getDataSystemTypeByL2FileName(file_name)
        head_info_conf = {
            "DataStartTime": start_time,        # L2 数据的开始时间
            "DataEndTime": end_time,            # L2 数据的结束时间
            "StationId": station_id,            # 站点ID
            "DeviceId": device_id,              # 设备ID
            "DataTypeL0": "",                   # L2级文件类型对应L0级文件类型
            "DbHeadTableL0": db_data_head_table + '_' + db_table_head_type + '_l0',
            "ValueType": value_type,
            "DataType": data_type,
            "SysType": db_table_head_type,
            "HeadId": head_id,
            "DeviceType": deviceType            # 0：激光雷达 or 1：原位气球
        }

        if deviceType == 0:
            # 激光雷达的逻辑：通过L2找L0数据
            #从L2文件头数据查找对应L0文件列表
            method = 1
            # 方法一：检索数据库方式（改进性能：多次查库修改为一次查库）
            if method == 0:
                s_time1 = time.time()    #计时
                data_type_l2 = file_name.split('_')[2]

                # L2 "T" --> L0 ["Rm_DV", "Ry_DV", "Fe_DE", "Fe_DN"]
                data_type_l0 = getL0SystemTypeByL22309(data_type_l2)
                print("L0级文件DataType: %s" % data_type_l0)
                if data_type_l0 == "":
                    print("未找到L2探测体制 %s 对应的L0体制文件" % data_type_l2)
                    return -1

                # head_info_conf = {
                #     "DataStartTime": start_time,        # L2 数据的开始时间
                #     "DataEndTime": end_time,            # L2 数据的结束时间
                #     "StationId": station_id,            # 站点ID
                #     "DeviceId": device_id,              # 设备ID
                #     "DataTypeL0": data_type_l0,         # L2级文件类型对应L0级文件类型
                #     "DbHeadTableL0": db_data_head_table + '_' + db_table_head_type + '_l0'
                # }
                head_info_conf['DataTypeL0'] = data_type_l0

                filepath_l0_dict = findL0FilesByL2FileName(head_info_conf)
                e_time1 = time.time()    #计时
                run_time1 = (e_time1 - s_time1) * 1000
                print('代码运行时间为%d ms' % run_time1)

            else:
                # 方法二：检索文件夹方式
                s_time2 = time.time()    #计时
                # L2_filepath = os.path.join(file_store_path, file_name)
                # L2_filepath = L2_filepath.replace("\\", "/")
                # 从L2构造对应的L0路径：raw_data/System1/2023-08-21/14/L2/D
                L0_path = os.path.join(file_store_path.split("L2")[0], "L0")
                L0_path = L0_path.replace("\\", "/")
                print("L2文件 + L0路径：%s; %s" % (L2_filepath, L0_path))
                filepath_l0_dict = find_L0_file(L2_filepath, L0_path)
                e_time2 = time.time()    #计时
                run_time2 = (e_time2 - s_time2) * 1000
                print('代码运行时间为%d ms' % run_time2)
        else:
            # 原位探测的逻辑：只用L2评定，不用L0
            filepath_l0_dict = {}

        # print(filepath_l0_dict)
        # for key in filepath_l0_dict.keys():
        #     print("key=%s, num=%d" % (key, len(filepath_l0_dict[key])))

        # 数据读取计时开始：
        start_time = time.time()
        if conf['MeasurementMethod'] == 0:
            # GUM算法评定过程（已完成）
            if deviceType == 1:
                # 原位气球GUM算法（原位气球不存在MCM算法）
                ret = algorithmGumBalloon2309(conf, L2_filepath, head_info_conf)
            elif deviceType == 2:
                # 原位火箭GUM算法（原位火箭不存在MCM算法）
                ret = algorithmGumRocket2309(conf, L2_filepath, head_info_conf)
            else:
                ret = algorithmGum2309(conf, L2_filepath, filepath_l0_dict, head_info_conf)

        elif conf['MeasurementMethod'] == 1:
            # MCM算法评定过程
            ret = algorithmMcm2309(conf, L2_filepath, filepath_l0_dict, head_info_conf)
            # ret = algorithmMcm2309_stub(conf, L2_filepath, filepath_l0_dict, head_info_conf)    #暂时测试用

        # time.sleep(10)   # 测试
        print("head_id: %d" % head_id)
        print(ret)
        # 数据读取计时结束：
        end_time = time.time()

        # 计算代码运行时间(ms)
        run_time = (end_time - start_time) * 1000
        if isinstance(ret, int):
            print("评定数量：%d, 本次评定数据量：%d" % (len(data_head_id_list), ret))
        else:
            print("Error: 评定数量：%d, 本次评定：%s" % (len(data_head_id_list), ret))
        print("评定时间：")
        print(run_time)

        # 如果评定结果的数据量为0，跳过设置
        if ret == 0:
            continue

        # 设置head表的一些标志位通知前端，以及统计站点评定数据
        # DataStatus：预处理后数据两个状态：1:已预处理；2：已评定
        # RawDataStatus： 原始数据两个状态：0:已录入；2：已评定
        data_process_status_flag = 1
        if conf['DataSource'] == 1:
            data_process_status_flag = 0
        # updateDataHeadTableV2(conf, head_id, 1, 2)
        print("更新head表评定状态：", conf['DataSource'], data_process_status_flag)
        updateDataHeadTableV2(conf, head_id, data_process_status_flag, 2)       # 即：该头信息对应为预处理数据1，状态为预处理完成1(根据数据来源判断修改DataStatus或RawDataStatus)
        # 更新设置Task表的任务Id，跟踪之前的batchId变化情况
        if 'LatestTaskId' in conf.keys():
            updateTaskKeyNameById(conf['LatestTaskId'], conf['TaskId'])
            updateTaskKeyNameByPreviousTaskKeyName(conf['PreviousTaskId'], conf['TaskId'])

            # 如果存库，表示headId被原始数据和预处理后数据共享
        if conf['SaveFlag'] == 1:
            # 评定计数
            ret = getHeadFullInfoByHeadIdV2(conf, head_id)
            print("更新数据头信息表状态(评定)：")
            print(ret)

            # 判断当前文件是否已评定过，决定是否计数到当前站点
            # if ret != "" and checkHeadIdIsPerformedV2(head_id, 1) is False:
            if ret != "" and checkHeadIdIsPerformed2309(conf, head_id, 1) is False:
                print("当前文件未有评定记录，计数！")
                station_id = ret['StationId']
                print(station_id)
                # 按文件记录评定数量
                updateStationDataCount(station_id, 1, 2)
                # updateStationDataCount(station_id, len(raw_data[0]), 2)

                # 更新SharedHeadId字段
                # updateDataHeadTableShareHeadId([str(head_id)], 2)
                updateDataHeadTableShareHeadId2309(conf, [str(head_id)], 2)

            # 更新SharedWay字段
            # updateDataHeadTableShareWay([str(head_id)], 2)
            updateDataHeadTableShareWay2309(conf, [str(head_id)], 2)

        # 测试
        # break
    return 0


# 激光雷达的GUM不确定度评定
def algorithmGum2309(conf, L2_filepath, filepath_l0_dict, head_info_conf):
    if filepath_l0_dict is None or len(filepath_l0_dict) == 0:
        print("未找到L0文件，L2对应的L0文件列表为空！跳过GUM评定！")
        return 0

    # 界面传参
    gum_parameter_k = conf['Factor']
    # 系统参数
    batch_id = conf['BatchId']
    # L2文件基本信息
    start_time = head_info_conf['DataStartTime']
    end_time = head_info_conf['DataEndTime']
    sys_type = head_info_conf['SysType']
    value_type = head_info_conf['ValueType']
    head_id = head_info_conf['HeadId']
    data_type = head_info_conf['DataType']     # 参量类型
    device_id = head_info_conf['DeviceId']     # 设备ID，获取设备参数使用
    #########

    # 接口测试
    system_param = {}
    system_param["lid_code"] = "LD1"  # 设备编号
    system_param["location_altitude"] = 1.35  # 系统所在海拔高度, km
    system_param["alt_angle"] = 60  # 斜向激光仰角, °
    system_param["height_resolution"] = 1  # 高度分辨率, km
    system_param["time_resolution"] = 60  # 时间分辨率, min

    system_param["ray_wavelen"] = 355  # 瑞利体制激光波长, nm
    system_param["u_ray_wavelen"] = 0  # 瑞利体制激光波长不确定度, nm
    system_param["ray_pulse_repeat"] = 30  # 瑞利体制激光重复频率, Hz
    system_param["ray_tau"] = 0  # 瑞利体制探测器渡越时间, ns
    system_param["u_ray_tau"] = 0  # 瑞利体制探测器渡越时间分散度, ns
    # system_param["z1"] = 89  # 密度积分起始高度, km
    # system_param["T1"] = 174.21  # 密度积分起始温度, K
    system_param["z1"] = 90  # 密度积分起始高度, km
    system_param["T1"] = 209.6  # 密度积分起始温度, K
    system_param["u_T1"] = 10  # 密度积分起始温度不确定度, K
    # system_param["z0"] = 61  # 参考密度所在高度, km
    # system_param["dens0"] = 2.819500e-04  # 参考密度, kg/m^3
    system_param["z0"] = 30  # 参考密度所在高度, km
    system_param["dens0"] = 0.01841  # 参考密度, kg/m^3
    system_param["u_dens0"] = 0.2  # 参考密度不确定度, %
    system_param["cRay_V_RV"] = 707  # 瑞利体制风场偏导系数

    system_param["ram_wavelen"] = 355  # 瑞利体制激光波长, nm
    system_param["u_ram_wavelen"] = 0  # 瑞利体制激光波长不确定度, nm
    system_param["ram_tau"] = 1e-9  # 拉曼体制渡越时间, ns
    system_param["u_ram_tau"] = 0  # 拉曼体制渡越时间分散度, ns
    system_param["ram_pulse_repeat"] = 30  # 拉曼体制激光重复频率, Hz
    system_param["poly_coef"] = [0.03, 0.05]  # 拉曼体制温度拟合系数

    system_param["res_wavelen"] = 355  # 荧光体制激光波长, nm
    system_param["u_res_wavelen"] = 0  # 荧光体制激光波长不确定度, nm
    system_param["res_pulse_repeat"] = 30  # 荧光体制激光重复频率, Hz
    system_param["res_tau"] = 0  # 荧光体制渡越时间, ns
    system_param["u_res_tau"] = 0  # 荧光体制渡越时间分散度, ns
    system_param["c_T_RT"] = 16  # 荧光体制温度对Rt偏导系数
    system_param["c_T_RV"] = 10  # 荧光体制温度对Rw偏导系数,0.00349
    system_param["cRes_V_RT"] = 0.00349  # 荧光体制风速对Rt偏导系数
    system_param["cRes_V_RV"] = 0.00349  # 荧光体制风速对Rw偏导系数
    system_param["table_T_resolution"] = 0.1  # 温风查算表温度分辨率
    system_param["table_V_resolution"] = 0.1  # 温风查算表风速分辨率

    # 数据库获取用户设置的参数
    print("默认值：", system_param)
    system_param = getUserSettingParam(device_id, system_param)
    print("替换后：", system_param)

    # 【测评计时】评定计时开始：
    s_time = time.time()
    # data_L0, uncertainty_df = evaluate_uncertainty(L2_filepath, filepath_l0_dict, system_param, k=gum_parameter_k)
    # print("不确定度评定参数：", L2_filepath, filepath_l0_dict, system_param, gum_parameter_k, data_type)
    data_L0, uncertainty_df = evaluate_uncertainty(L2_filepath, filepath_l0_dict, system_param, k=gum_parameter_k, given_param=data_type)
    # 评定结果为空
    if uncertainty_df is None:
        print("错误：GUM评定结果为None!")
        return -1
    # show_L0_data(data_L0, 'D', lid_code, output_dirpath='ResultPic')
    # uncertainty = uncertainty_df.values
    # show_uncertainty(uncertainty, 'D', lid_code, output_dirpath='ResultPic')
    # 【测评计时】数据读取计时结束：
    e_time = time.time()
    run_time = (e_time - s_time) * 1000    # 计算代码运行时间(ms)
    msg = "[GUM] runtime: %.3f ms; start: %.3f; end: %.3f; head_id: %d; file: %s" % (run_time, s_time, e_time, head_id, L2_filepath)
    write_into_file("run_time_"+conf['TaskId']+".txt", msg)

    print("========================GUM")
    print(L2_filepath)
    print(data_L0)
    print("@@@@@@@@@@@@@@@@@@@@@@@@GUM")
    print(uncertainty_df)
    # print(uncertainty_df.empty)   # 使用uncertainty_df.empty也可以判断（True为空，False为不空）
    if uncertainty_df.shape[0] == 0:
        print("错误：GUM评定结果为空值（Empty DataFrame）!")
        return -1
    #
    #
    # print("----------")
    # print(uncertainty_df.head())
    print("==========")
    print(uncertainty_df.columns)
    # print(uncertainty_df.columns[1])
    # print("&&&&&&&&&&")
    # print(uncertainty_df.info())
    # print("##########")

    # 获取L2数据拼接不确定度值，保存入数据库表
    # 1.传参获取：SysType = s1-s6；HeadId，BatchId，DataStartTime，DataEndTime
    # 2.从文件中获取体制:Label
    # 3.返回数据中获取Height，Value， GumResult，GumResultExtended， GumParameterK，U1-U6，McmParameterP， McmResult

    value_arr = []
    # 结果标识key
    col_header = uncertainty_df.columns
    result_label = {
        "height": col_header[0],
        "value": col_header[1],
        "gum": col_header[2],
        "gum_extend": col_header[3],
        # "u1": col_header[4],
    }

    # 列头：['高度(km)', '温度(K)', 'U(K)_GUM扩展不确定度', 'u(K)_标准不确定度', 'u1(K)_探测点光子噪声',
    #        'u2(K)_参考点光子噪声', 'u3(K)_背景噪声', 'u4(K)_探测点渡越时间', 'u5(K)_参考点渡越时间',
    #        'u6(K)_参考温度', 'u7(K)_f0通道光子噪声', 'u8(K)_f+通道光子噪声', 'u9(K)_f-通道光子噪声',
    #        'u10(K)_f0通道背景噪声', 'u11(K)_f+通道背景噪声', 'u12(K)_f-通道背景噪声',
    #        'u13(K)_f0通道渡越时间', 'u14(K)_f+通道渡越时间', 'u15(K)_f-通道渡越时间',
    #        'u16(K)_温风查算表温度分辨率', 'u17(K)_拉曼体制高阶通道光子噪声', 'u18(K)_拉曼体制低阶通道光子噪声',
    #        'u19(K)_拉曼体制高阶通道背景噪声', 'u20(K)_拉曼体制低阶通道背景噪声', 'u21(K)_拉曼体制高阶通道渡越时间',
    #        'u22(K)_拉曼体制低阶通道渡越时间'],
    for index,value in uncertainty_df.iterrows():
        # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
        # print("检查状态库表，是否停止程序..")
        if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
            return 0

        print(index)
        raw_data = {}
        raw_data['Height'] = value[result_label['height']]
        raw_data['Value'] = value[result_label['value']]
        # 去掉探测值为NaN的情况
        if np.isnan(raw_data['Value']):
            continue

        raw_data['SysType'] = sys_type
        raw_data['HeadId'] = head_id
        raw_data['BatchId'] = batch_id
        raw_data['ValueType'] = value_type
        raw_data['Label'] = 0   # L2数据头
        raw_data['ParameterSetting'] = -1
        raw_data['ParameterDescription'] = -1
        raw_data['DataStartTime'] = start_time
        raw_data['DataEndTime'] = end_time

        # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据
        if conf['DataSource'] != 3:
            raw_data['IsAbnormal'] = 0

        # raw_data['McmResult'] = 0
        # raw_data['McmParameterP'] = 0
        # raw_data['McmParameterExpTimes'] = 0

        # # 计算结果填充到原数据结构中
        # U(K)_GUM扩展不确定度  u(K)_标准不确定度  u1(K)_探测点光子噪声
        # u2(K)_参考点光子噪声  u3(K)_背景噪声  u4(K)_探测点死区时间  u5(K)_参考点死区时间  u6(K)_参考温度
        raw_data['GumResult'] = value[result_label['gum']]
        raw_data['GumResultExtended'] = value[result_label['gum_extend']]
        raw_data['GumParameterK'] = gum_parameter_k
        ind = 0
        while ind < len(col_header):
            if ind < 4:
                ind += 1
                continue
            # raw_data['U'+ str(ind-3)] = value[col_header[ind]]
            pattern = r'u(\d+)\('  # 匹配u1(K)_XXX中的数字
            match = re.search(pattern, col_header[ind])
            if match:
                u_index = match.group(1)
                # print("激光雷达映射U值：%s, %s" % (col_header[ind], u_index))
                raw_data['U'+ u_index] = value[col_header[ind]]
            else:
                raw_data['U'+ str(ind-3)] = value[col_header[ind]]
            ind += 1

        # print(raw_data)
        value_arr.append(raw_data)

        # 测试
        # break

    # 保存到数据库
    print("存储GUM评定结果..HeadId = %d (l2: %d), 评定数量：%d" % (head_id, head_id, len(value_arr)))
    # # 根据headid先删除之前评定的结果
    # 第一版逻辑：使用数据库中的数据（已经包含了之前的评定结果如GUM/MCM，更新本次的结果，删除历史记录，重新存库
    # 第二版逻辑：不删除，直接更新库 TODO:是否有问题？
    # delete_pre_data2309(conf['DbSaveTable'], [str(head_id)], sys_type)
    # # time.sleep(30)
    # replace into更新策略，使用headId+heigh作为唯一判断主键
    # saveAlgorithmResult(value_arr, conf)
    # 采用insert into xxx on duplicate key update机制更新MySQL数据库，构造update后的内容
    # 类似：GumResult=VALUES(GumResult),GumResultExtended=VALUES(GumResultExtended),GumParameterK=VALUES(GumParameterK),U1=VALUES(U1),U2=VALUES(U2),U3=VALUES(U3), ...

    # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据，需更新到数据库
    if conf['DataSource'] != 3:
        #mysql
        # on_update_condition = "BatchId = VALUES(BatchId), IsAbnormal = VALUES(IsAbnormal), GumResult = VALUES(GumResult), GumResultExtended = VALUES(GumResultExtended), GumParameterK = VALUES(GumParameterK)"
        # 人大金仓数据库不能用VALUES()
        on_update_condition = "BatchId = BatchId, IsAbnormal = IsAbnormal, GumResult = GumResult, GumResultExtended = GumResultExtended, GumParameterK = GumParameterK"
    else:
        #mysql
        # on_update_condition = "BatchId = VALUES(BatchId), GumResult = VALUES(GumResult), GumResultExtended = VALUES(GumResultExtended), GumParameterK = VALUES(GumParameterK)"
        # 人大金仓数据库不能用VALUES()
        on_update_condition = "BatchId = BatchId, GumResult = GumResult, GumResultExtended = GumResultExtended, GumParameterK = GumParameterK"
    ind = 0
    while ind < len(col_header):
        if ind < 4:
            ind += 1
            continue
        # on_update_condition += ', U' + str(ind -3) + '= VALUES(U' + str(ind-3) + ')'
        pattern = r'u(\d+)\('  # 匹配u1(K)_XXX中的数字
        match = re.search(pattern, col_header[ind])
        if match:
            u_index = match.group(1)
            # print("激光雷达映射U值：%s, %s" % (col_header[ind], u_index))
            #mysql
            # on_update_condition += ', U' + u_index + '= VALUES(U' + u_index + ')'
            # 人大金仓数据库不能用VALUES()
            on_update_condition += ', U' + u_index + '= U' + u_index
        else:
            #mysql
            # on_update_condition += ', U' + str(ind -3) + '= VALUES(U' + str(ind-3) + ')'
            # 人大金仓数据库不能用VALUES()
            on_update_condition += ', U' + str(ind -3) + '= U' + str(ind-3)
        ind += 1
    conf['OnUpdateCondition'] = on_update_condition
    # print("##############测试##############：", conf)
    saveAlgorithmResult2309(value_arr, conf)
    # # time.sleep(30)

    # 202309算法逻辑不再需要不确定度分量文件？？
    # # 更新不确定度分量文件到HeadId信息
    # print("存储评定不确定度分量文件名称..")
    # db_head_table = db_data_head_table if conf['SaveFlag'] == 1 else db_temporary_data_head_table
    # print(db_data_head_table, head_id_l2, uncertainty_component_filename)
    # updateHeadTableUncertaintyComponentFileByHeadId(db_head_table, head_id_l2, uncertainty_component_filename)

    return len(value_arr)


# 激光雷达的MCM不确定度评定
def algorithmMcm2309(conf, L2_filepath, filepath_l0_dict, head_info_conf):
    if filepath_l0_dict is None or len(filepath_l0_dict) == 0:
        print("未找到L0文件，L2对应的L0文件列表为空！跳过MCM评定！")
        return 0

    # 界面传参
    mcm_parameter_P = conf['McmParameterP']
    mcm_parameter_exp_time = conf['McmParameterExpTimes']
    # 系统参数
    batch_id = conf['BatchId']
    # L2文件基本信息
    start_time = head_info_conf['DataStartTime']
    end_time = head_info_conf['DataEndTime']
    sys_type = head_info_conf['SysType']
    value_type = head_info_conf['ValueType']
    head_id = head_info_conf['HeadId']
    data_type = head_info_conf['DataType']     # 参量类型

    #########
    # MCM接口测试
    system_param = {}
    system_param["lid_code"] = "LD1"  # 设备编号
    system_param["location_altitude"] = 1.35  # 系统所在海拔高度, km
    system_param["alt_angle"] = 90  # 斜向激光仰角, °
    system_param["height_resolution"] = 1  # 高度分辨率, km
    system_param["time_resolution"] = 60  # 时间分辨率, min

    system_param["ray_wavelen"] = 355  # 瑞利体制激光波长, nm
    system_param["u_ray_wavelen"] = 0  # 瑞利体制激光波长不确定度, nm
    system_param["ray_pulse_repeat"] = 30  # 瑞利体制激光重复频率, Hz
    system_param["ray_tau"] = 0  # 瑞利体制探测器渡越时间, ns
    system_param["u_ray_tau"] = 0  # 瑞利体制探测器渡越时间分散度, ns
    system_param["z1"] = 89  # 密度积分起始高度, km
    system_param["T1"] = 174.21  # 密度积分起始温度, K
    system_param["u_T1"] = 10  # 密度积分起始温度不确定度, K
    system_param["z0"] = 61  # 参考密度所在高度, km
    system_param["dens0"] = 2.819500e-04  # 参考密度, kg/m^3
    system_param["u_dens0"] = 0  # 参考密度不确定度, %
    system_param["cRay_V_RV"] = 707  # 瑞利体制风场偏导系数

    system_param["ram_wavelen"] = 355  # 瑞利体制激光波长, nm
    system_param["u_ram_wavelen"] = 0  # 瑞利体制激光波长不确定度, nm
    system_param["ram_tau"] = 1e-9  # 拉曼体制渡越时间, ns
    system_param["u_ram_tau"] = 0  # 拉曼体制渡越时间分散度, ns
    system_param["ram_pulse_repeat"] = 30  # 拉曼体制激光重复频率, Hz
    system_param["poly_coef"] = [0.03, 0.05]  # 拉曼体制温度拟合系数

    system_param["res_wavelen"] = 355  # 荧光体制激光波长, nm
    system_param["u_res_wavelen"] = 0  # 荧光体制激光波长不确定度, nm
    system_param["res_pulse_repeat"] = 30  # 荧光体制激光重复频率, Hz
    system_param["res_tau"] = 0  # 荧光体制渡越时间, ns
    system_param["u_res_tau"] = 0  # 荧光体制渡越时间分散度, ns
    system_param["c_T_RT"] = 16  # 荧光体制温度对Rt偏导系数
    system_param["c_T_RV"] = 10  # 荧光体制温度对Rw偏导系数,0.00349
    system_param["cRes_V_RT"] = 0.00349  # 荧光体制风速对Rt偏导系数
    system_param["cRes_V_RV"] = 0.00349  # 荧光体制风速对Rw偏导系数
    system_param["table_T_resolution"] = 0.1  # 温风查算表温度分辨率
    system_param["table_V_resolution"] = 0.1  # 温风查算表风速分辨率

    # 【测评计时】评定计时开始：
    s_time = time.time()
    data_L0, uncertainty_df = evaluate_MCM_uncertainty(L2_filepath, filepath_l0_dict, system_param, k=mcm_parameter_P)
    # 评定结果为空
    if uncertainty_df is None:
        print("错误：MCM评定结果为None!")
        return -1
    # 【测评计时】数据读取计时结束：
    e_time = time.time()
    run_time = (e_time - s_time) * 1000    # 计算代码运行时间(ms)
    msg = "[MCM] runtime: %.3f ms; start: %.3f; end: %.3f; head_id: %d; file: %s" % (run_time, s_time, e_time, head_id, L2_filepath)
    write_into_file("run_time_"+conf['TaskId']+".txt", msg)

    print("========================MCM")
    print(L2_filepath)
    print(data_L0)
    print("@@@@@@@@@@@@@@@@@@@@@@@@MCM")
    print(uncertainty_df)
    # print(uncertainty_df.empty)   # 使用uncertainty_df.empty也可以判断（True为空，False为不空）
    if uncertainty_df.shape[0] == 0:
        print("错误：MCM评定结果为空值（Empty DataFrame）!")
        return -1
    #
    #
    # print("----------MCM")
    # print(uncertainty_df.head())
    # print("==========MCM")
    # print(uncertainty_df.columns)
    # print("&&&&&&&&&&MCM")
    # print(uncertainty_df.info())
    # print("##########MCM")

    value_arr = []
    result_label = {
        "height": uncertainty_df.columns[0],
        "value": uncertainty_df.columns[1],
        "mcm": uncertainty_df.columns[2]
    }
    # 列头：高度(km)     温度(K)  U(K)_MCM不确定度
    for index,value in uncertainty_df.iterrows():
        # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
        # print("检查状态库表，是否停止程序..")
        if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
            return 0

        print(index)
        raw_data = {}
        raw_data['Height'] = value[result_label['height']]
        raw_data['Value'] = value[result_label['value']]
        # 去掉探测值为NaN的情况
        if np.isnan(raw_data['Value']):
            continue

        raw_data['SysType'] = sys_type
        raw_data['HeadId'] = head_id
        raw_data['BatchId'] = batch_id
        raw_data['ValueType'] = value_type
        raw_data['Label'] = 0   # L2数据头
        raw_data['ParameterSetting'] = -1
        raw_data['ParameterDescription'] = -1
        raw_data['DataStartTime'] = start_time
        raw_data['DataEndTime'] = end_time

        # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据
        if conf['DataSource'] != 3:
            raw_data['IsAbnormal'] = 0

        # MCM测试值（在GUM基础上上下浮动5%），需替换为甲方的接口
        raw_data['McmResult'] = value[result_label['mcm']]
        raw_data['McmParameterP'] = mcm_parameter_P;  # 如0.95
        raw_data['McmParameterExpTimes'] = mcm_parameter_exp_time  # 如200000

        print(raw_data)
        value_arr.append(raw_data)

    # 保存到数据库
    print("存储MCM评定结果..HeadId = %d (l2: %d), 评定数量：%d" % (head_id, head_id, len(value_arr)))
    # # 根据headid先删除之前评定的结果
    # 第一版逻辑：使用数据库中的数据（已经包含了之前的评定结果如GUM/MCM，更新本次的结果，删除历史记录，重新存库
    # 第二版逻辑：不删除，直接更新库 TODO:是否有问题？
    # delete_pre_data2309(conf['DbSaveTable'], [str(head_id)], sys_type)

    # saveAlgorithmResult(value_arr, conf)
    # 采用insert into xxx on duplicate key update机制更新MySQL数据库，构造update后的内容

    # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据，需更新到数据库
    if conf['DataSource'] != 3:
        # mysql
        # conf['OnUpdateCondition'] = "BatchId = VALUES(BatchId), IsAbnormal = VALUES(IsAbnormal), McmResult = VALUES(McmResult), McmParameterP = VALUES(McmParameterP), McmParameterExpTimes = VALUES(McmParameterExpTimes)"
        # 人大金仓数据库不能用VALUES()
        conf['OnUpdateCondition'] = "BatchId = BatchId, IsAbnormal = IsAbnormal, McmResult = McmResult, McmParameterP = McmParameterP, McmParameterExpTimes = McmParameterExpTimes"
    else:
        #mysql
        # conf['OnUpdateCondition'] = "BatchId = VALUES(BatchId), McmResult = VALUES(McmResult), McmParameterP = VALUES(McmParameterP), McmParameterExpTimes = VALUES(McmParameterExpTimes)"
        # 人大金仓数据库不能用VALUES()
        conf['OnUpdateCondition'] = "BatchId = BatchId, McmResult = McmResult, McmParameterP = McmParameterP, McmParameterExpTimes = McmParameterExpTimes"
    saveAlgorithmResult2309(value_arr, conf)

    return len(value_arr)


# 原位气球探测的GUM不确定度评定
def algorithmGumBalloon2309(conf, L2_filepath, head_info_conf):
    # 界面传参
    gum_parameter_k = conf['Factor']
    # 系统参数
    batch_id = conf['BatchId']
    # L2文件基本信息
    start_time = head_info_conf['DataStartTime']
    end_time = head_info_conf['DataEndTime']
    sys_type = head_info_conf['SysType']
    value_type = head_info_conf['ValueType']
    head_id = head_info_conf['HeadId']
    data_type = head_info_conf['DataType']     # 参量类型
    device_id = head_info_conf['DeviceId']     # 设备ID，获取设备参数使用
    #########

    # 接口测试
    system_param = {}
    system_param["T_error"] = 0.3           # 温度载荷的最大允许误差，表示正负0.3（输入）
    system_param["k_square"] = 3            # 系统所在海拔高度, km
    system_param["param_u2"] = 0            # 太阳辐射引入的测量不确定度分量（输入）
    system_param["Vd"] = 8                  # 探空仪上升的速度（输入）（m/s）
    system_param["cp"] = 1.004              # 空气的定压比热（单位：kJ/(kg·K)）
    system_param["t"] = 3                   # 传感器响应时间（输入）（s）
    system_param["t_high_ratio"] = "(0,11]km:-0.007;(11,20]km:0.00;(20,32]km:0.001"      #温度随高度的变化率（输入）
    system_param["param_u5"] = 0            # 探空仪传感结构引入的测量不确定度分量
    system_param["t0"] = 1                  # 探空仪数据采样周期（s）
    system_param['deltaV'] = 2              # 风速系数(m/s),默认：2m/s

    # 数据库获取用户设置的参数
    print("默认值：", system_param)
    system_param = getUserSettingParam(device_id, system_param)
    print("替换后：", system_param)

    # 【测评计时】评定计时开始：
    s_time = time.time()
    uncertainty_df = evaluate_uncertainty_balloon(L2_filepath, system_param, k=gum_parameter_k, given_param=data_type)
    # 评定结果为空
    if uncertainty_df is None:
        print("错误：GUM评定结果为None!")
        return -1

    # 【测评计时】数据读取计时结束：
    e_time = time.time()
    run_time = (e_time - s_time) * 1000    # 计算代码运行时间(ms)
    msg = "[GUM-Balloon] runtime: %.3f ms; start: %.3f; end: %.3f; head_id: %d; file: %s" % (run_time, s_time, e_time, head_id, L2_filepath)
    write_into_file("run_time_"+conf['TaskId']+".txt", msg)

    # show_L0_data(data_L0, 'D', lid_code, output_dirpath='ResultPic')
    # uncertainty = uncertainty_df.values
    # show_uncertainty(uncertainty, 'D', lid_code, output_dirpath='ResultPic')

    print("========================Balloon-GUM")
    print(L2_filepath)
    print("@@@@@@@@@@@@@@@@@@@@@@@@Balloon-GUM")
    print(uncertainty_df)
    # print(uncertainty_df.empty)   # 使用uncertainty_df.empty也可以判断（True为空，False为不空）
    if uncertainty_df.shape[0] == 0:
        print("错误：GUM-Balloon评定结果为空值（Empty DataFrame）!")
        return -1
    #
    #
    # print("----------")
    # print(uncertainty_df.head())
    print("==========Balloon-GUM")
    print(uncertainty_df.columns)
    # print(uncertainty_df.columns[1])
    # print("&&&&&&&&&&")
    # print(uncertainty_df.info())
    # print("##########")
    # return

    # 获取L2数据拼接不确定度值，保存入数据库表
    # 1.传参获取：SysType = s1-s6；HeadId，BatchId，DataStartTime，DataEndTime
    # 2.从文件中获取体制:Label
    # 3.返回数据中获取Height，Value， GumResult，GumResultExtended， GumParameterK，U1-U6，McmParameterP， McmResult
    # return 0

    value_arr = []
    # 结果标识key
    col_header = uncertainty_df.columns
    result_label = {
        "height": col_header[0],
        "value": col_header[1],
        "gum": col_header[2],
        "gum_extend": col_header[3],
        # "u1": col_header[4],
    }

    # 列头根据不同的探测参量类型合并如下：
    # 温度：  "u1(K)_由温度传感器自身准确度引入",  "u2(K)_由太阳辐射引入", "u3(K)_由气动效应引入", "u4(K)_由传感器响应时间引入", "u5(K)_由探空仪传感结构引入", "u6(K)_由数据传输引入", "u7(K)_由数据处理引入"
    # 压力：  "u1(K)_由压力传感器自身准确度引入",  "u2(K)_由温度特性引入", "u3(K)_由气动效应引入", "u4(K)_由传感器响应时间引入", "u5(K)_由数据传输引入", "u6(K)_由数据处理引入"
    # 风场：  "u1(K)_由气球速度测量引入", "u2(K)_由大气风速解算引入", "u3(K)_由气球风阻系数引入"
    #
    # 列头：['高度(km)', '温度(K)', 'U(K)_GUM扩展不确定度', 'u(K)_标准不确定度',
    #         "u1(K)_由温度传感器自身准确度引入",       温度u1
    #         "u2(K)_由太阳辐射引入",                   温度u2
    #         "u3(K)_由压力传感器自身准确度引入",               压力u1
    #         "u4(K)_由温度特性引入",                           压力u2
    #         "u5(K)_由气动效应引入",                   温度u3  压力u3
    #         "u6(K)_由传感器响应时间引入",             温度u4  压力u4
    #         "u7(K)_由探空仪传感结构引入",             温度u5
    #         "u8(K)_由数据传输引入",                   温度u6  压力u5
    #         "u9(K)_由数据处理引入",                   温度u7  压力u6
    #         "u10(K)_由气球速度测量引入",                              风场u1
    #         "u11(K)_由大气风速解算引入",                              风场u2
    #         "u12(K)_由气球风阻系数引入",                              风场u3
    #        ]
    # 优化处理：可以把探空气球和探空火箭的u值融合，共17个，如下：
    # 列头：['高度(km)', '温度(K)', 'U(K)_GUM扩展不确定度', 'u(K)_标准不确定度',
    #         "u1(K)_由温度传感器自身准确度引入",       B温度u1                        R温度u1
    #         "u2(K)_由太阳辐射引入",                   B温度u2                        R温度u2
    #         "u3(K)_由压力传感器自身准确度引入",                B压力u1                        R压力u1
    #         "u4(K)_由温度特性引入",                            B压力u2                        R压力u2
    #         "u5(K)_由气动效应引入",                   B温度u3  B压力u3               R温度u3  R压力u3
    #         "u6(K)_由传感器响应时间引入",             B温度u4  B压力u4
    #         "u7(K)_由迟滞效应引入",                                                  R温度u4
    #         "u8(K)_由探空仪传感结构引入",             B温度u5                        R温度u5
    #         "u9(K)_由数据传输引入",                   B温度u6  B压力u5
    #         "u10(K)_由数据处理引入",                  B温度u7  B压力u6
    #         "u11(K)_由气球速度测量引入",                                B风场u1
    #         "u12(K)_由大气风速解算引入",                                B风场u2
    #         "u13(K)_由气球风阻系数引入",                                B风场u3
    #         "u14(K)_由风压引入",                                                                R压力u4
    #         "u15(K)_由火箭速度测量引入",                                                                 R风场u1
    #         "u16(K)_由大气风速解算引入",                                                                 R风场u2
    #         "u17(K)_由火箭风阻系数引入",                                                                 R风场u3
    #        ]
    for index,value in uncertainty_df.iterrows():
        # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
        # print("检查状态库表，是否停止程序..")
        if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
            return 0

        print(index)
        raw_data = {}
        raw_data['Height'] = value[result_label['height']]
        raw_data['Value'] = value[result_label['value']]
        # 去掉探测值为NaN的情况
        if np.isnan(raw_data['Value']):
            continue

        raw_data['SysType'] = sys_type
        raw_data['HeadId'] = head_id
        raw_data['BatchId'] = batch_id
        raw_data['ValueType'] = value_type
        raw_data['Label'] = 0   # L2数据头
        raw_data['ParameterSetting'] = -1
        raw_data['ParameterDescription'] = -1
        raw_data['DataStartTime'] = start_time
        raw_data['DataEndTime'] = end_time

        # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据
        if conf['DataSource'] != 3:
            raw_data['IsAbnormal'] = 0

        # raw_data['McmResult'] = 0
        # raw_data['McmParameterP'] = 0
        # raw_data['McmParameterExpTimes'] = 0

        # # 计算结果填充到原数据结构中
        # U(K)_GUM扩展不确定度  u(K)_标准不确定度  u1(K)_探测点光子噪声
        # u2(K)_参考点光子噪声  u3(K)_背景噪声  u4(K)_探测点死区时间  u5(K)_参考点死区时间  u6(K)_参考温度
        raw_data['GumResult'] = value[result_label['gum']]
        raw_data['GumResultExtended'] = value[result_label['gum_extend']]
        raw_data['GumParameterK'] = gum_parameter_k
        ind = 0
        while ind < len(col_header):
            if ind < 4:
                ind += 1
                continue
            # raw_data['U'+ str(ind-3)] = value[col_header[ind]]
            pattern = r'u(\d+)\('  # 匹配u1(K)_XXX中的数字
            match = re.search(pattern, col_header[ind])
            if match:
                u_index = match.group(1)
                # print("原位气球映射U值：%s, %s" % (col_header[ind], u_index))
                raw_data['U'+ u_index] = value[col_header[ind]]
            else:
                raw_data['U'+ str(ind-3)] = value[col_header[ind]]
            ind += 1

        # print(raw_data)
        value_arr.append(raw_data)

        # 测试
        # break

    # 保存到数据库
    print("存储气球探测GUM评定结果..HeadId = %d (l2: %d), 评定数量：%d" % (head_id, head_id, len(value_arr)))
    # # 根据headid先删除之前评定的结果
    # 第一版逻辑：使用数据库中的数据（已经包含了之前的评定结果如GUM/MCM，更新本次的结果，删除历史记录，重新存库
    # 第二版逻辑：不删除，直接更新库 TODO:是否有问题？
    # delete_pre_data2309(conf['DbSaveTable'], [str(head_id)], sys_type)
    # # time.sleep(30)
    # replace into更新策略，使用headId+heigh作为唯一判断主键
    # saveAlgorithmResult(value_arr, conf)
    # 采用insert into xxx on duplicate key update机制更新MySQL数据库，构造update后的内容
    # 类似：GumResult=VALUES(GumResult),GumResultExtended=VALUES(GumResultExtended),GumParameterK=VALUES(GumParameterK),U1=VALUES(U1),U2=VALUES(U2),U3=VALUES(U3), ...

    # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据，需更新到数据库
    if conf['DataSource'] != 3:
        #mysql
        # on_update_condition = "BatchId = VALUES(BatchId), IsAbnormal = VALUES(IsAbnormal), GumResult = VALUES(GumResult), GumResultExtended = VALUES(GumResultExtended), GumParameterK = VALUES(GumParameterK)"
        # 人大金仓数据库不能用VALUES()
        on_update_condition = "BatchId = BatchId, IsAbnormal = IsAbnormal, GumResult = GumResult, GumResultExtended = GumResultExtended, GumParameterK = GumParameterK"
    else:
        #mysql
        # on_update_condition = "BatchId = VALUES(BatchId), GumResult = VALUES(GumResult), GumResultExtended = VALUES(GumResultExtended), GumParameterK = VALUES(GumParameterK)"
        # 人大金仓数据库不能用VALUES()
        on_update_condition = "BatchId = BatchId, GumResult = GumResult, GumResultExtended = GumResultExtended, GumParameterK = GumParameterK"

    ind = 0
    while ind < len(col_header):
        if ind < 4:
            ind += 1
            continue
        # on_update_condition += ', U' + str(ind -3) + '= VALUES(U' + str(ind-3) + ')'
        pattern = r'u(\d+)\('  # 匹配u1(K)_XXX中的数字
        match = re.search(pattern, col_header[ind])
        if match:
            u_index = match.group(1)
            # print("原位气球映射U值：%s, %s" % (col_header[ind], u_index))
            #mysql
            # on_update_condition += ', U' + u_index + '= VALUES(U' + u_index + ')'
            # 人大金仓数据库不能用VALUES()
            on_update_condition += ', U' + u_index + '= U' + u_index
        else:
            #mysql
            # on_update_condition += ', U' + str(ind -3) + '= VALUES(U' + str(ind-3) + ')'
            # 人大金仓数据库不能用VALUES()
            on_update_condition += ', U' + str(ind -3) + '= U' + str(ind-3)
        ind += 1
    conf['OnUpdateCondition'] = on_update_condition
    # print("##############测试##############：", conf)
    saveAlgorithmResult2309(value_arr, conf)
    # # time.sleep(30)

    # 202309算法逻辑不再需要不确定度分量文件？？
    # # 更新不确定度分量文件到HeadId信息
    # print("存储评定不确定度分量文件名称..")
    # db_head_table = db_data_head_table if conf['SaveFlag'] == 1 else db_temporary_data_head_table
    # print(db_data_head_table, head_id_l2, uncertainty_component_filename)
    # updateHeadTableUncertaintyComponentFileByHeadId(db_head_table, head_id_l2, uncertainty_component_filename)

    return len(value_arr)


# 原位火箭探测的GUM不确定度评定
def algorithmGumRocket2309(conf, L2_filepath, head_info_conf):
    # 界面传参
    gum_parameter_k = conf['Factor']
    # 系统参数
    batch_id = conf['BatchId']
    # L2文件基本信息
    start_time = head_info_conf['DataStartTime']
    end_time = head_info_conf['DataEndTime']
    sys_type = head_info_conf['SysType']
    value_type = head_info_conf['ValueType']
    head_id = head_info_conf['HeadId']
    data_type = head_info_conf['DataType']     # 参量类型
    device_id = head_info_conf['DeviceId']     # 设备ID，获取设备参数使用
    #########

    # 接口测试
    system_param = {}
    system_param["T_error"] = "(0,50]km:3.5;(50,70]km:0.35"         # 温度载荷的最大允许误差（输入），默认："(0,50]km:3.5;(50,70]km:0.35"
    system_param["k_square"] = 3                                    # 系统所在海拔高度, km
    system_param["day_time"] = 1                                    # 火箭释放时间：1白天，0晚上
    system_param["T_u2_delta_radio"] = "(0,45]km:0.8;(45,55]km:1.0;(55,60]km:1.5;(60,70]km:2.3"         # 辐射最大修正量（适用于白天day_time=1）
    system_param["delta_r_param"] = "(0,40]km:4e-4;(40,50]km:1.6e-3;(50,60]km:8e-3;(60,70]km:10e-3"     # 恢复系数误差（未用）
    system_param["Vz"] = "8"                                        # 下降速度(m/s)（按速度仿真结果读取，未提供）（T未用，P使用）
    system_param["delta_Vz"] = "0.03"                               # 下降速度的精度(m/s)（T未用，P使用）
    system_param["cp"] = 1.004                                      # 空气的定压比热（单位：kJ/(kg·K)）（T未用）
    system_param["T_u3_delta_aerodynamic"] = "(0,30]km:0.1;(30,40]km:0.6;(40,45]km:1.3;(45,50]km:3.0;(50,55]km:5.1;(55,60]km:6.3;(60,70]km:9.0"   # 气动最大修正值
    system_param["is_correct"] = 0                                  # 是否已做修正(0未修正，1：已修正)
    system_param["u4_sluggish_param"] = "(0,50]km:0.1;(50,58]km:0.4;(58,60]km:0.9;(60,70]km:6.0"        # 迟滞影响量参数
    system_param["T_param_u5"] = 0                                  # 探空仪传感结构引入的测量不确定度分量

    system_param["P_error_param"] = "(1,1000]Pa:10;(1000,110000]Pa:50"          # 压力传感器的最大允许误差
    system_param["P_u2_delta_T_features"] = 0                                   # 传感器温度特性半宽度
    system_param["P_param_u3"] = 0                                              # 传感器气动效应引入的测量不确定度分量
    system_param["P_u4_p"] = 1.29                                               # 空气密度（kg/m3）

    system_param['delta_t'] = 0.001                                 # 时间间隔测量的不确定度
    system_param['deltaV'] = 0.3                                    # 风速系数

    # 数据库获取用户设置的参数
    print("默认值：", system_param)
    system_param = getUserSettingParam(device_id, system_param)
    print("替换后：", system_param)

    # 【测评计时】评定计时开始：
    s_time = time.time()
    uncertainty_df = evaluate_uncertainty_rocket(L2_filepath, system_param, k=gum_parameter_k, given_param=data_type)
    # 评定结果为空
    if uncertainty_df is None:
        print("错误：GUM评定结果为None!")
        return -1
    # 【测评计时】数据读取计时结束：
    e_time = time.time()
    run_time = (e_time - s_time) * 1000    # 计算代码运行时间(ms)
    msg = "[GUM-Rocket] runtime: %.3f ms; start: %.3f; end: %.3f; head_id: %d; file: %s" % (run_time, s_time, e_time, head_id, L2_filepath)
    write_into_file("run_time_"+conf['TaskId']+".txt", msg)

    # show_L0_data(data_L0, 'D', lid_code, output_dirpath='ResultPic')
    # uncertainty = uncertainty_df.values
    # show_uncertainty(uncertainty, 'D', lid_code, output_dirpath='ResultPic')

    print("========================Rocket-GUM")
    print(L2_filepath)
    print("@@@@@@@@@@@@@@@@@@@@@@@@Rocket-GUM")
    print(uncertainty_df)
    # print(uncertainty_df.empty)   # 使用uncertainty_df.empty也可以判断（True为空，False为不空）
    if uncertainty_df.shape[0] == 0:
        print("错误：GUM-Rocket评定结果为空值（Empty DataFrame）!")
        return -1
    #
    #
    # print("----------")
    # print(uncertainty_df.head())
    print("==========Rocket-GUM")
    print(uncertainty_df.columns)
    # print(uncertainty_df.columns[1])
    # print("&&&&&&&&&&")
    # print(uncertainty_df.info())
    # print("##########")
    # return

    # 获取L2数据拼接不确定度值，保存入数据库表
    # 1.传参获取：SysType = s1-s6；HeadId，BatchId，DataStartTime，DataEndTime
    # 2.从文件中获取体制:Label
    # 3.返回数据中获取Height，Value， GumResult，GumResultExtended， GumParameterK，U1-U6，McmParameterP， McmResult
    # return 0

    value_arr = []
    # 结果标识key
    col_header = uncertainty_df.columns
    result_label = {
        "height": col_header[0],
        "value": col_header[1],
        "gum": col_header[2],
        "gum_extend": col_header[3],
        # "u1": col_header[4],
    }

    # 列头根据不同的探测参量类型合并如下：
    # 温度： "u1(K)_由温度传感器自身准确度引入", "u2(K)_由太阳辐射引入", "u3(K)_由气动效应引入", "u4(K)_由迟滞效应引入", "u5(K)_由探空仪传感结构引入"
    # 压力： "u1(K)_由压力传感器自身准确度引入", "u2(K)_由温度特性引入", "u3(K)_由气动效应引入", "u4(K)_由风压引入"
    # 密度： [不评]
    # 风场： "u1(K)_由火箭速度测量引入", "u2(K)_由大气风速解算引入", "u3(K)_由火箭风阻系数引入",
    #
    # 列头：['高度(km)', '温度(K)', 'U(K)_GUM扩展不确定度', 'u(K)_标准不确定度',
    #         "u1(K)_由温度传感器自身准确度引入",       R温度u1
    #         "u2(K)_由太阳辐射引入",                   R温度u2
    #         "u3(K)_由压力传感器自身准确度引入",                R压力u1
    #         "u4(K)_由温度特性引入",                            R压力u2
    #         "u5(K)_由气动效应引入",                   R温度u3  R压力u3
    #         "u6(K)_由迟滞效应引入",                   R温度u4
    #         "u7(K)_由探空仪传感结构引入",             R温度u5
    #         "u8(K)_由风压引入",                                R压力u4
    #         "u9(K)_由火箭速度测量引入",                                 R风场u1
    #         "u10(K)_由大气风速解算引入",                                R风场u2
    #         "u11(K)_由火箭风阻系数引入",                                R风场u3
    #        ]
    #
    # 优化处理：可以把探空气球和探空火箭的u值融合，共17个，如下：
    # 列头：['高度(km)', '温度(K)', 'U(K)_GUM扩展不确定度', 'u(K)_标准不确定度',
    #         "u1(K)_由温度传感器自身准确度引入",       B温度u1                        R温度u1
    #         "u2(K)_由太阳辐射引入",                   B温度u2                        R温度u2
    #         "u3(K)_由压力传感器自身准确度引入",                B压力u1                        R压力u1
    #         "u4(K)_由温度特性引入",                            B压力u2                        R压力u2
    #         "u5(K)_由气动效应引入",                   B温度u3  B压力u3               R温度u3  R压力u3
    #         "u6(K)_由传感器响应时间引入",             B温度u4  B压力u4
    #         "u7(K)_由迟滞效应引入",                                                  R温度u4
    #         "u8(K)_由探空仪传感结构引入",             B温度u5                        R温度u5
    #         "u9(K)_由数据传输引入",                   B温度u6  B压力u5
    #         "u10(K)_由数据处理引入",                  B温度u7  B压力u6
    #         "u11(K)_由气球速度测量引入",                                B风场u1
    #         "u12(K)_由大气风速解算引入",                                B风场u2
    #         "u13(K)_由气球风阻系数引入",                                B风场u3
    #         "u14(K)_由风压引入",                                                                R压力u4
    #         "u15(K)_由火箭速度测量引入",                                                                 R风场u1
    #         "u16(K)_由大气风速解算引入",                                                                 R风场u2
    #         "u17(K)_由火箭风阻系数引入",                                                                 R风场u3
    #        ]
    #
    for index,value in uncertainty_df.iterrows():
        # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
        # print("检查状态库表，是否停止程序..")
        if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
            return 0

        print(index)
        raw_data = {}
        raw_data['Height'] = value[result_label['height']]
        raw_data['Value'] = value[result_label['value']]
        # 去掉探测值为NaN的情况
        if np.isnan(raw_data['Value']):
            continue

        raw_data['SysType'] = sys_type
        raw_data['HeadId'] = head_id
        raw_data['BatchId'] = batch_id
        raw_data['ValueType'] = value_type
        raw_data['Label'] = 0   # L2数据头
        raw_data['ParameterSetting'] = -1
        raw_data['ParameterDescription'] = -1
        raw_data['DataStartTime'] = start_time
        raw_data['DataEndTime'] = end_time

        # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据
        if conf['DataSource'] != 3:
            raw_data['IsAbnormal'] = 0

        # raw_data['McmResult'] = 0
        # raw_data['McmParameterP'] = 0
        # raw_data['McmParameterExpTimes'] = 0

        # # 计算结果填充到原数据结构中
        # U(K)_GUM扩展不确定度  u(K)_标准不确定度  u1(K)_探测点光子噪声
        # u2(K)_参考点光子噪声  u3(K)_背景噪声  u4(K)_探测点死区时间  u5(K)_参考点死区时间  u6(K)_参考温度
        raw_data['GumResult'] = value[result_label['gum']]
        raw_data['GumResultExtended'] = value[result_label['gum_extend']]
        raw_data['GumParameterK'] = gum_parameter_k
        ind = 0
        while ind < len(col_header):
            if ind < 4:
                ind += 1
                continue
            # raw_data['U'+ str(ind-3)] = value[col_header[ind]]
            pattern = r'u(\d+)\('  # 匹配u1(K)_XXX中的数字
            match = re.search(pattern, col_header[ind])
            if match:
                u_index = match.group(1)
                # print("原位火箭映射U值：%s, %s" % (col_header[ind], u_index))
                raw_data['U'+ u_index] = value[col_header[ind]]
            else:
                raw_data['U'+ str(ind-3)] = value[col_header[ind]]
            ind += 1

        print(raw_data)
        value_arr.append(raw_data)

        # 测试
        # break

    # 保存到数据库
    print("存储火箭探测GUM评定结果..HeadId = %d (l2: %d), 评定数量：%d" % (head_id, head_id, len(value_arr)))
    # # 根据headid先删除之前评定的结果
    # 第一版逻辑：使用数据库中的数据（已经包含了之前的评定结果如GUM/MCM，更新本次的结果，删除历史记录，重新存库
    # 第二版逻辑：不删除，直接更新库 TODO:是否有问题？
    # delete_pre_data2309(conf['DbSaveTable'], [str(head_id)], sys_type)
    # # time.sleep(30)
    # replace into更新策略，使用headId+heigh作为唯一判断主键
    # saveAlgorithmResult(value_arr, conf)
    # 采用insert into xxx on duplicate key update机制更新MySQL数据库，构造update后的内容
    # 类似：GumResult=VALUES(GumResult),GumResultExtended=VALUES(GumResultExtended),GumParameterK=VALUES(GumParameterK),U1=VALUES(U1),U2=VALUES(U2),U3=VALUES(U3), ...

    # 202309逻辑：当数据源为最后一次预处理数据时，维持原来的IsAbnormal值，其他情况，全部重置为0，即默认都为正常数据，需更新到数据库
    if conf['DataSource'] != 3:
        #mysql
        # on_update_condition = "BatchId = VALUES(BatchId), IsAbnormal = VALUES(IsAbnormal), GumResult = VALUES(GumResult), GumResultExtended = VALUES(GumResultExtended), GumParameterK = VALUES(GumParameterK)"
        # 人大金仓数据库不能用VALUES()
        on_update_condition = "BatchId = BatchId, IsAbnormal = IsAbnormal, GumResult = GumResult, GumResultExtended = GumResultExtended, GumParameterK = GumParameterK"
    else:
        #mysql
        # on_update_condition = "BatchId = VALUES(BatchId), GumResult = VALUES(GumResult), GumResultExtended = VALUES(GumResultExtended), GumParameterK = VALUES(GumParameterK)"
        # 人大金仓数据库不能用VALUES()
        on_update_condition = "BatchId = BatchId, GumResult = GumResult, GumResultExtended = GumResultExtended, GumParameterK = GumParameterK"
    ind = 0
    while ind < len(col_header):
        if ind < 4:
            ind += 1
            continue
        # on_update_condition += ', U' + str(ind -3) + '= VALUES(U' + str(ind-3) + ')'
        pattern = r'u(\d+)\('  # 匹配u1(K)_XXX中的数字
        match = re.search(pattern, col_header[ind])
        if match:
            u_index = match.group(1)
            # print("原位火箭映射U值：%s, %s" % (col_header[ind], u_index))
            #mysql
            # on_update_condition += ', U' + u_index + '= VALUES(U' + u_index + ')'
            # 人大金仓数据库不能用VALUES()
            on_update_condition += ', U' + u_index + '= U' + u_index
        else:
            #mysql
            # on_update_condition += ', U' + str(ind -3) + '= VALUES(U' + str(ind-3) + ')'
            # 人大金仓数据库不能用VALUES()
            on_update_condition += ', U' + str(ind -3) + '= U' + str(ind-3)
        ind += 1
    conf['OnUpdateCondition'] = on_update_condition
    # print("##############测试##############：", conf)
    saveAlgorithmResult2309(value_arr, conf)
    # # time.sleep(30)

    # 202309算法逻辑不再需要不确定度分量文件？？
    # # 更新不确定度分量文件到HeadId信息
    # print("存储评定不确定度分量文件名称..")
    # db_head_table = db_data_head_table if conf['SaveFlag'] == 1 else db_temporary_data_head_table
    # print(db_data_head_table, head_id_l2, uncertainty_component_filename)
    # updateHeadTableUncertaintyComponentFileByHeadId(db_head_table, head_id_l2, uncertainty_component_filename)

    return len(value_arr)


# MCM测试数据（改自GUM，仅用于临时测试）
# def algorithmMcm2309_stub(conf, L2_filepath, filepath_l0_dict, head_info_conf):
#     # 界面传参
#     mcm_parameter_P = conf['McmParameterP']
#     mcm_parameter_exp_time = conf['McmParameterExpTimes']
#     # 系统参数
#     batch_id = conf['BatchId']
#     # L2文件基本信息
#     start_time = head_info_conf['DataStartTime']
#     end_time = head_info_conf['DataEndTime']
#     sys_type = head_info_conf['SysType']
#     value_type = head_info_conf['ValueType']
#     head_id = head_info_conf['HeadId']
#     #########
#     # 接口测试
#     system_param = {}
#     system_param['lid_code'] = 'LD1'                 #设备编号
#     system_param['location_altitude'] = 1.35         #系统所在海拔高度
#     system_param['alt_angle'] = 90                   #斜向激光仰角
#     system_param['height_resolution'] = 1            #高度分辨率
#     system_param['time_resolution'] = 3600           #时间分辨率
#     system_param['ray_wavelen'] = 355        #瑞利体制激光波长
#     system_param['u_ray_wavelen'] = 0        #瑞利体制激光波长不确定度
#     system_param['ray_pulse_repeat'] = 30    #瑞利体制激光重复频率
#     system_param['ray_tau'] = 0              #瑞利体制探测器渡越时间
#     system_param['u_ray_tau'] = 0            #瑞利体制探测器渡越时间不确定度
#     system_param['z1'] = 89         #密度积分起始高度
#     system_param['T1'] = 174.21         #密度积分起始温度
#     system_param['u_T1'] = 0         #密度积分起始温度不确定度
#     system_param['z0'] = 61         #参考密度所在高度
#     system_param['dens0'] = 2.819500e-04         #参考密度
#     system_param['u_dens0'] = 0         #参考密度不确定度
#     system_param['cRay_V_RV'] = 707         #瑞利体制风场偏导系数
#     system_param['ram_wavelen'] = 355         #瑞利体制激光波长
#     system_param['u_ram_wavelen'] = 0         #瑞利体制激光波长不确定度
#     system_param['ram_tau'] = 1e-9         #拉曼体制渡越时间
#     system_param['u_ram_tau'] = 0         #拉曼体制渡越时间不确定度
#     system_param['ram_pulse_repeat'] = 30         #拉曼体制激光重复频率
#     system_param['poly_coef'] = [0.03,0.05]        #拉曼体制温度拟合系数
#     system_param['res_wavelen'] = 355       #荧光体制激光波长
#     system_param['u_res_wavelen'] = 0    #荧光体制激光波长不确定度
#     system_param['res_pulse_repeat'] = 30   #荧光体制激光重复频率
#     system_param['res_tau'] = 0             #荧光体制渡越时间
#     system_param['u_res_tau'] = 0               #荧光体制渡越时间不确定度
#     system_param['c_T_RT'] = 0.00349         #荧光体制温度对Rt偏导系数
#     system_param['c_T_RV'] = 0.00349         #荧光体制温度对Rw偏导系数
#     system_param['cRes_V_RT'] = 0.00349        #荧光体制风速对Rt偏导系数
#     system_param['cRes_V_RV'] = 0.00349        #荧光体制风速对Rw偏导系数
#     data_L0, uncertainty_df = evaluate_uncertainty(L2_filepath, filepath_l0_dict, system_param, k=mcm_parameter_P)
#     # 评定结果为空
#     if uncertainty_df is None:
#         print("错误：构造MCM评定结果为None!")
#         return -1
#     value_arr = []
#     # 列头：高度(km)     温度(K)  U(K)_GUM扩展不确定度  u(K)_标准不确定度  u1(K)_探测点光子噪声
#     # u2(K)_参考点光子噪声  u3(K)_背景噪声  u4(K)_探测点死区时间  u5(K)_参考点死区时间  u6(K)_参考温度
#     for index,value in uncertainty_df.iterrows():
#         # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
#         print("检查状态库表，是否停止程序..")
#         if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
#             return 0
#
#         print(index)
#         raw_data = {}
#         raw_data['Height'] = value['高度(km)']
#         raw_data['Value'] = value['温度(K)']
#
#         raw_data['SysType'] = sys_type
#         raw_data['HeadId'] = head_id
#         raw_data['BatchId'] = batch_id
#         raw_data['ValueType'] = value_type
#         raw_data['Label'] = 0   # L2数据头
#         raw_data['ParameterSetting'] = -1
#         raw_data['ParameterDescription'] = -1
#         raw_data['DataStartTime'] = start_time
#         raw_data['DataEndTime'] = end_time
#
#         # MCM测试值（在GUM基础上上下浮动5%），需替换为甲方的接口
#         random_num = get_random_int_value(-50,50) / 1000
#         raw_data['McmResult'] = str_to_float(value['u(K)_标准不确定度']) * (1 + random_num)
#         raw_data['McmParameterP'] = mcm_parameter_P;  # 如0.95
#         raw_data['McmParameterExpTimes'] = mcm_parameter_exp_time  # 如200000
#
#         print(raw_data)
#         value_arr.append(raw_data)
#
#     # 保存到数据库
#     print("存储MCM评定结果..HeadId = %d (l2: %d), 评定数量：%d" % (head_id, head_id, len(value_arr)))
#     delete_pre_data2309(conf['DbSaveTable'], [str(head_id)], sys_type)
#     saveAlgorithmResult(value_arr, conf)
#
#     return len(value_arr)


def performUncertaintyAlgorithm(data_head_id_list, conf, TASK_ID):
    """
    data_head_id_list: 评定不确定度的数据对象
    data_level: 数据类型L0 or L2
    input_data_loc: 0:不存库，预处理的数据在临时表，1:存库，预处理的数据在正式预处理后的表
    """
    # 计数本次评定的所有数据条数
    print("数据评定对象：")
    # 根据head_id，测量数据类型value_type，获取原始数据
    for (head_id, value_type, start_time, end_time) in data_head_id_list:
        print("head_id = %d, value_type = %s" % (head_id, value_type))
        # raw_data = getPreprocessedDataByHeadId(head_id, input_data_loc, conf['DataLevel'], conf)               # data_type指定从L0或L2原始数据表中获取数据
        raw_data = getPreprocessedDataByHeadIdV3(head_id, conf)
        if raw_data == "":
            print("错误：未找到最近录入的原始数据")
            # return {"code": -1, "msg": "未找到最近录入的原始数据！"}
            continue

        print("raw_data:XXXXXXXX")
        print(raw_data)
        # time.sleep(2)
        # continue
        # 获取评定的对象，GUM算法中根据评定的对象找L0对应的体制
        if len(raw_data) > 0:
            conf['Label'] = raw_data[0]['Label']
        else:
            print("错误：未找到L2数据")
            continue


        # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
        # print("all_检查状态库表，是否停止程序..")
        if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
            return 0

        # 获取存储目标表的数据（防止GUM和MCM结果相互冲掉）TODO
        # raw_data_pre = getPreprocessedDataByHeadIdV3(head_id, conf, 1)

        # 如果缺失DataType信息，需要获取headId的DataType信息
        # 或者DataType为多个探测体制
        print("参数探测体制：", conf['DataType'])
        if conf['DataType'] == "":
            print("获取raw_data探测体制...")
            head_info = getHeadFullInfoByHeadIdV2(conf, head_id)
            print(head_info)
            conf['DataType'] = head_info['DataType']

        # 数据读取计时开始：
        start_time = time.time()
        if conf['MeasurementMethod'] == 0:
            # GUM算法评定过程（已完成）
            ret = algorithmGum(raw_data, conf)

        elif conf['MeasurementMethod'] == 1:
            # 评定MCM算法，需要获取headId的DataType信息
            # head_info = getHeadFullInfoByHeadId(head_id)
            # head_info = getHeadFullInfoByHeadIdV2(conf, head_id)
            # print(head_info)
            # conf['DataType'] == head_info['DataType']

            # MCM算法评定过程
            ret = algorithmMcm(raw_data, conf)

        print("head_id: %d" % head_id)
        print(ret)
        # 数据读取计时结束：
        end_time = time.time()

        # 计算代码运行时间(ms)
        run_time = (end_time - start_time) * 1000
        if isinstance(ret, int):
            print("评定数量：%d, 本次评定数据量：%d" % (len(data_head_id_list), ret))
        else:
            print("Error: 评定数量：%d, 本次评定：%s" % (len(data_head_id_list), ret))
        print("评定时间：")
        print(run_time)
        #

        # 测试
        # time.sleep(2)
        # break

        # 更新数据头信息表状态（1.更新状态，2.更新batchId）
        # updateDataHeadTable(head_id, conf['TaskId'], 1, 2)      # 即：该头信息对应为预处理数据1，状态为评定完成2

        # 待验证
        # if ret <= 0:
        #     print("当前headId未有效评定，跳过。。。")

        updateDataHeadTableV2(conf, head_id, 1, 2)       # 即：该头信息对应为预处理数据1，状态为预处理完成1
        # 更新设置Task表的任务Id，跟踪之前的batchId变化情况
        if 'LatestTaskId' in conf.keys():
            updateTaskKeyNameById(conf['LatestTaskId'], conf['TaskId'])
            updateTaskKeyNameByPreviousTaskKeyName(conf['PreviousTaskId'], conf['TaskId'])

            # 如果存库，表示headId被原始数据和预处理后数据共享
        if conf['SaveFlag'] == 1:
            # 评定计数
            ret = getHeadFullInfoByHeadIdV2(conf, head_id)
            print("更新数据头信息表状态(评定)：")
            print(ret)

            # 判断当前文件是否已评定过，决定是否计数到当前站点
            if ret != "" and checkHeadIdIsPerformedV2(head_id, 1) is False:
                print("当前文件未有评定记录，计数！")
                station_id = ret['StationId']
                print(station_id)
                # 按文件记录评定数量
                updateStationDataCount(station_id, 1, 2)
                # updateStationDataCount(station_id, len(raw_data[0]), 2)

                updateDataHeadTableShareHeadId([str(head_id)], 2)

            # 更新SharedWay字段
            updateDataHeadTableShareWay([str(head_id)], 2)

        # 测试
        # break
    return 0


##############################################
# 设备参数和名称对应关系（TODO:可放入数据库）
# device_param = {
#     # 激光雷达参数中文名称及参数名称
#     '系统所在海拔高度': 'location_altitude',
#     '斜向激光仰角': 'alt_angle',
#     '高度分辨率': 'height_resolution',
#     '时间分辨率': 'time_resolution',
#     '瑞利体制激光波长': 'ray_wavelen',
#     '瑞利体制激光波长不确定度': 'u_ray_wavelen',
#     '瑞利体制激光重复频率': 'ray_pulse_repeat',
#     '瑞利体制探测器渡越时间': 'ray_tau',
#     '瑞利体制探测器渡越时间分散度': 'u_ray_tau',
#     '密度积分起始高度': 'z1',
#
#     '密度积分起始温度': 'T1',
#     '密度积分起始温度不确定度': 'u_T1',
#     '参考密度所在高度': 'z0',
#     '参考密度': 'dens0',
#     '参考密度不确定度': 'u_dens0',
#     '瑞利体制风场偏导系数': 'cRay_V_RV',
#     '瑞利体制激光波长': 'ram_wavelen',
#     '瑞利体制激光波长不确定度': 'u_ram_wavelen',
#     '拉曼体制渡越时间': 'ram_tau',
#     '拉曼体制渡越时间分散度': 'u_ram_tau',
#
#     '拉曼体制激光重复频率': 'ram_pulse_repeat',
#     '拉曼体制温度拟合系数': 'poly_coef',
#     '荧光体制激光波长': 'res_wavelen',
#     '荧光体制激光波长不确定度': 'u_res_wavelen',
#     '荧光体制激光重复频率': 'res_pulse_repeat',
#     '荧光体制渡越时间': 'res_tau',
#     '荧光体制渡越时间分散度': 'u_res_tau',
#     '荧光体制温度对Rt偏导系数': 'c_T_RT',
#     '荧光体制温度对Rw偏导系数': 'c_T_RV',
#     '荧光体制风速对Rt偏导系数': 'cRes_V_RT',
#
#     '荧光体制风速对Rw偏导系数': 'cRes_V_RV',
#     '温风查算表温度分辨率': 'table_T_resolution',
#     '温风查算表风速分辨率': 'table_V_resolution',
#
#     # 原位气球参数中文名称及参数名称
#     'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#     # 'xxxxxxx': 'xxx',
#
# #     system_param["T_error"] = 0.3           # 温度载荷的最大允许误差，表示正负0.3（输入）
# # system_param["k_square"] = 3            # 系统所在海拔高度, km
# # system_param["param_u2"] = 0            # 太阳辐射引入的测量不确定度分量（输入）
# # system_param["Vd"] = 8                  # 探空仪上升的速度（输入）（m/s）
# # system_param["cp"] = 1.004              # 空气的定压比热（单位：kJ/(kg·K)）
# # system_param["t"] = 3                   # 传感器响应时间（输入）（s）
# # system_param["t_high_ratio"] = "(0,11]km:-0.007;(11,20]km:0.00;(20,32]km:0.001"      #温度随高度的变化率（输入）
# # system_param["param_u5"] = 0            # 探空仪传感结构引入的测量不确定度分量
# # system_param["t0"] = 1                  # 探空仪数据采样周期（s）
# # system_param['deltaV'] = 2              # 风速系数(m/s),默认：2m/s
#
#
#     # 原位火箭参数中文名称及参数名称
#
# #     system_param["T_error"] = "(0,50]km:3.5;(50,70]km:0.35"         # 温度载荷的最大允许误差（输入），默认："(0,50]km:3.5;(50,70]km:0.35"
# # system_param["k_square"] = 3                                    # 系统所在海拔高度, km
# # system_param["day_time"] = 1                                    # 火箭释放时间：1白天，0晚上
# # system_param["T_u2_delta_radio"] = "(0,45]km:0.8;(45,55]km:1.0;(55,60]km:1.5;(60,70]km:2.3"         # 辐射最大修正量（适用于白天day_time=1）
# # system_param["delta_r_param"] = "(0,40]km:4e-4;(40,50]km:1.6e-3;(50,60]km:8e-3;(60,70]km:10e-3"     # 恢复系数误差（未用）
# # system_param["Vz"] = "8"                                        # 下降速度(m/s)（按速度仿真结果读取，未提供）（T未用，P使用）
# # system_param["delta_Vz"] = "0.03"                               # 下降速度的精度(m/s)（T未用，P使用）
# # system_param["cp"] = 1.004                                      # 空气的定压比热（单位：kJ/(kg·K)）（T未用）
# # system_param["T_u3_delta_aerodynamic"] = "(0,30]km:0.1;(30,40]km:0.6;(40,45]km:1.3;(45,50]km:3.0;(50,55]km:5.1;(55,60]km:6.3;(60,70]km:9.0"   # 气动最大修正值
# # system_param["is_correct"] = 0                                  # 是否已做修正(0未修正，1：已修正)
# # system_param["u4_sluggish_param"] = "(0,50]km:0.1;(50,58]km:0.4;(58,60]km:0.9;(60,70]km:6.0"        # 迟滞影响量参数
# # system_param["T_param_u5"] = 0                                  # 探空仪传感结构引入的测量不确定度分量
# #
# # system_param["P_error_param"] = "(1,1000]Pa:10;(1000,110000]Pa:50"          # 压力传感器的最大允许误差
# # system_param["P_u2_delta_T_features"] = 0                                   # 传感器温度特性半宽度
# # system_param["P_param_u3"] = 0                                              # 传感器气动效应引入的测量不确定度分量
# # system_param["P_u4_p"] = 1.29                                               # 空气密度（kg/m3）
# #
# # system_param['delta_t'] = 0.001                                 # 时间间隔测量的不确定度
# # system_param['deltaV'] = 0.3                                    # 风速系数
#
# }


# 获取用户设置的设备参数，未设置的使用默认值代替
def getUserSettingParam(device_id, system_param):
    print("获取用户设置的设备参数：")
    user_param = getDeviceParametersUserSettingById2309(device_id)
    #print(user_param)

    # 每个参数，如：{'Id': 693, 'StationId': 1, 'DeviceId': 1, 'ParameterId': 0, 'ParameterCode': 'location_altitude', 'Parameter': '系统所在海拔高度', 'Value': '1.35', 'Unit': 'km', 'Distribution': ''}
    # 如果用户设置了参数，替换默认参数值
    for param_item in user_param:
        if param_item['Value'] != '':
            param_code = param_item['ParameterCode']
            if param_code in system_param.keys():    # 合法的参数项
                system_param[param_code] = obj_to_example_var(system_param[param_code], param_item['Value'])

    return system_param


##############################################
# 根据数据录入函数checkSystemType()对应
# 和数据库表sys_dict_system_type内容一致
systemTypeL0 = {
    1: "532R",          #拉曼
    2: "532P",          #瑞利
    3: "589P",          #钠荧光
    4: "374P",          #铁荧光
}
# 对应甲方提供的GUM计算文件中“表3. 文件类型对应关系”
# l2_type：L2级文件类型；
# l1_type：对应L0级文件类型；
# channelInfo：通道信息（详细信息从systemTypeL2ChannelInfo中获取）
systemTypeL2 = {
    101: {"l2_type": "TSVP", "l1_type": "532R", "channelInfo": ["TempRam"]},                        # 下平流层温度 （对应L0: 532R）
    102: {"l2_type": "HWLS", "l1_type": "532P", "channelInfo": ["MeridiWindSL", "ZonalWindSL"]},    # 下平流层水平风速（对应L0: 532P）
    103: {"l2_type": "DUSM", "l1_type": "532P", "channelInfo": ["DenRay"]},                         # 上平流层和中间层密度（对应L0: 532P）
    104: {"l2_type": "TUSM", "l1_type": "532P", "channelInfo": ["TempRay"]},                        # 上平流层和中间层温度（对应L0: 532P）
    105: {"l2_type": "HWSM", "l1_type": "532P", "channelInfo": ["MeridiWindSM", "ZonalWindSM"]},    # 上平流层和中间层水平风速（对应L0: 532P）
    106: {"l2_type": "NDNA", "l1_type": "589P", "channelInfo": ["NaDen"]},                          # 钠原子数密度（对应L0: 589P）
    107: {"l2_type": "TMSL", "l1_type": "589P", "channelInfo": ["TempNa"]},                         # 钠层温度（对应L0: 589P）
    108: {"l2_type": "HWSL", "l1_type": "589P", "channelInfo": ["ZonalWind", "MeridiWind"]},        # 钠层水平风速（对应L0: 589P）
    # 以下三类不从头进行不确定度计算，而是使用其他数据的不确定度结果进行融合
    # 具体融合公式甲方未提供，后续再考虑
    109: {"l2_type": "DCSM", "l1_type": "", "channelInfo": []},       # 大气密度拼接（对应L0: ???）
    110: {"l2_type": "TCSM", "l1_type": "", "channelInfo": []},       # 大气温度拼接（对应L0: ???）
    111: {"l2_type": "WCSM", "l1_type": "", "channelInfo": []},       # 大气风场拼接（对应L0: ???）
}

# 新数据（202309）系统1 的L2和L0对应关系
#   L2体制	                    L0体制
# T / Temperature(K) 	        Rm_DV（RaLJP、RaHJP）
#                               Ry_DV（RayVDP、RayVNP）
#                               Fe_DE（FeF-EP、FeF0EP1、FeF+EP、FeF0EP2）
#                               Fe_DN（FeF-NP、FeF0NP1、FeF+NP、FeF0NP2）
# D / Density(kg*m-3)	        Ry_DV（RayVDP、RayVNP）
# W / MeridiWind(m*s-1) 	    Ry_DN（RayNS1P、RayNS2P、FeF+NP、FeF0NP2）   //径向风W2
#                               Fe_DN（FeF-NP、FeF0NP1、FeF+NP、FeF0NP2）
# W / ZonalWind(m*s-1)                                                       //纬向风W1
# P / xxx
# N / xxx
# F / xxx
systemTypeL2SystemOne2309 = {
    1: {"l2_type": "T", "l1_type": ["Rm_DV", "Ry_DV", "Fe_DE", "Fe_DN"], "channelInfo": ["Temperature(K)"]},   # 温度 （对应L0: "Rm_DV", "Ry_DV", "Fe_DE", "Fe_DN"）
    2: {"l2_type": "P", "l1_type": [], "channelInfo": ["xxx", "xxx"]},                      # 压力
    3: {"l2_type": "W1", "l1_type":[], "channelInfo": ["xxx"]},                             # 纬向风（对应L0: ）
    4: {"l2_type": "W2", "l1_type":["Ry_DN", "Fe_DN"], "channelInfo": ["xxx"]},                         # 径向风（对应L0: "Ry_DN", "Fe_DN"）
    5: {"l2_type": "D", "l1_type": ["Ry_DV"], "channelInfo": ["Density(kg*m-3)"]},           #密度
    6: {"l2_type": "N", "l1_type": [], "channelInfo": ["NaDen"]},                                               # 钠原子数密度（对应L0: ）
    7: {"l2_type": "F", "l1_type": [], "channelInfo": ["FeDensity(cm-3)"]},    # 铁原子密度（对应L0: ）
}
# 通过systemTypeL2SystemOne2309中字段l1_type（L0的文件名类型）的值来查询对应的通道信息和不确定度分量文件类型
# key: 需评定的数据列名；TODO：？？？1 列？2 列？
# l1_col_measure：对应数据列（通道）名；
# measure_file_type： 不确定度分量文件类型
systemTypeL1ChannelInfoByL0FileType = {
    "Rm_DV": {"l1_col_measure": ["RaLJP", "RaHJP"], "measure_file_type": "RamT"},    #TODO: measure_file_type为临时值
    "Ry_DV": {"l1_col_measure": ["RayVDP", "RayVNP"], "measure_file_type": "RayD"},
    "Fe_DE": {"l1_col_measure": ["FeF-EP", "FeF0EP1", "FeF+EP", "FeF0EP2"], "measure_file_type": "RamT"},  #??
    "Fe_DN": {"l1_col_measure": ["FeF-NP", "FeF0NP1", "FeF+NP", "FeF0NP2"], "measure_file_type": "RamT"},  #??
    "Ry_DN": {"l1_col_measure": ["RayNS1P", "RayNS2P", "FeF+NP", "FeF0NP2"], "measure_file_type": "RayD"}, #??
    "Fe_DN": {"l1_col_measure": ["FeF-NP", "FeF0NP1", "FeF+NP", "FeF0NP2"], "measure_file_type": "ResD"},  #??
    "Ry_DV": {"l1_col_measure": ["RayVDP", "RayVNP"], "measure_file_type": "RayD"},   #??
    "x":        {"l1_col_measure": ["xxx", "xxx", "xxx"], "measure_file_type": "ResD"},
    "xx":       {"l1_col_measure": ["xxx", "xxx", "xxx"], "measure_file_type": "ResT"},
    "xxx":    {"l1_col_measure": ["xxx", "xxx", "xxx"], "measure_file_type": "ResW"},
    "xxxx":   {"l1_col_measure": ["xxx", "xxx", "xxx"], "measure_file_type": "ResW"},
}

# TODO 新数据（202309）系统2 的L2和L0对应关系
systemTypeL2SystemTwo2309 = {
}

# TODO 新数据（202309）系统3 的L2和L0对应关系
systemTypeL2SystemThree2309 = {
}

# 通过systemTypeL2中字段channelInfo的值来查询对应的通道信息和不确定度分量文件类型
# key: 需评定的数据列名；
# l1_col_measure：对应数据列（通道）名；
# measure_file_type： 不确定度分量文件类型
systemTypeL2ChannelInfo = {
    "TempRam":      {"l1_col_measure": ["RR1", "RR2"], "measure_file_type": "RamT"},
    "MeridiWindSL": {"l1_col_measure": ["RayNLS", "RayNLR"], "measure_file_type": "RayW"},
    "ZonalWindSL":  {"l1_col_measure": ["RayWLS", "RayWLR"], "measure_file_type": "RayW"},
    "DenRay":       {"l1_col_measure": ["RayVLS"], "measure_file_type": "RayD"},
    "TempRay":      {"l1_col_measure": ["RayVHS"], "measure_file_type": "RayT"},
    "MeridiWindSM": {"l1_col_measure": ["RayNHS", "RayNHR"], "measure_file_type": "RayW"},
    "ZonalWindSM":  {"l1_col_measure": ["RayWHS", "RayWHR"], "measure_file_type": "RayW"},
    "NaDen":        {"l1_col_measure": ["NaVF0", "NaVF1", "NaVF2"], "measure_file_type": "ResD"},
    "TempNa":       {"l1_col_measure": ["NaVF0", "NaVF1", "NaVF2"], "measure_file_type": "ResT"},
    "ZonalWind":    {"l1_col_measure": ["NaWF0", "NaWF1", "NaWF2"], "measure_file_type": "ResW"},
    "MeridiWind":   {"l1_col_measure": ["NaNF0", "NaNF1", "NaNF2"], "measure_file_type": "ResW"},
}

systemTypeL2AndL0Relation = {
    101: 1,        #下平流层温度 （对应L0: 532R）
    102: 2,        #下平流层水平风速（对应L0: 532P）
    103: 2,        #上平流层和中间层密度（对应L0: 532P）
    104: 2,        #上平流层和中间层温度（对应L0: 532P）
    105: 2,        #上平流层和中间层水平风速（对应L0: 532P）
    106: 3,        #钠原子数密度（对应L0: 589P）
    107: 3,        #钠层温度（对应L0: 589P）
    108: 3,        #钠层水平风速（对应L0: 589P）
    109: -1,
    110: -1,
    111: -1,
}


# 探测体制和探测值体制对应关系
systemDataType2Label = {
    "TSVP": "TempRam",
    "HWLS": "MeridiWindSL",     # MeridiWindSL, ZonalWindSL
    "DUSM": "DenRay",
    "TUSM": "TempRay",
    "HWSM": "ZonalWindSM",      # ZonalWindSM, MeridiWindSM
    "NDNA": "NaDen",
    "TMSL": "TempNa",
    "HWSL": "ZonalWind",        # ZonalWind, MeridiWind
    "DCSM": "DenAll",
    "TCSM": "TempAll",
    "WCSM": "ZonalWind",        # ZonalWind, MeridiWind
}


def getL0SytemTypeByL2(var_value):
    """
    var_value: L2 体制代码
    返回：L0体制代码，可直接匹配数据库
    """
    ret = -1
    try:
        ret = systemTypeL2AndL0Relation[var_value]
    except Exception as e:
        print("getL0SytemTypeByL2_Error: %s" % e)
        return -1

    return ret


def getL0SystemTypeFromL2(var_value):
    for key in systemTypeL2.keys():
        value = systemTypeL2[key]
        if value['l2_type'] == var_value:
            return value['l1_type']
    return ""


# 针对2023年9月新数据类型
# l1_type 匹配 bt_data_head表的DataType字段
# input: "T"
# output:  ["Rm_DV", "Ry_DV", "Fe_DE", "Fe_DN"]
def getL0SystemTypeFromL22309(var_value):
    for key in systemTypeL2SystemOne2309.keys():
        item = systemTypeL2SystemOne2309[key]
        if item['l2_type'] == var_value:
            return item['l1_type']
    return ""


def getL0SystemTypeByL22309(var_value):
    for key in systemTypeL2AndL02309.keys():
        item = systemTypeL2AndL02309[key]
        if item['l2_type'] == var_value:
            return item['l1_type']
    return ""


def getSystemType(var_value, type):
    """
    var_value: 探测体制代码
    type: 数据类型：2:L2, 0:L0
    """
    if type == 2:
        return systemTypeL2[var_value]      # L2的信息更多，获取后要进一步解析
    elif type == 0:
        return systemTypeL0[var_value]
    else:
        return ""


# 逻辑：
# L2文件名（体制：TSVP） -->L2数据列头/l2_col_measure（TempRam）--> 对应的L0数据列头（RR1、RR2）
def getChannelInfo(l2_col_measure):
    """
    l2_col_measure: L2 文件需要评定的列
    返回：对应的L0文件列（通道名），及不确定度分量文件名
    """
    if l2_col_measure in systemTypeL2ChannelInfo.keys():
        return systemTypeL2ChannelInfo[l2_col_measure]
    else:
        return ""


# 查找逻辑：
# TODO：临时测试数据
def getChannelInfo2309(l2_col_measure):
    """
    l2_col_measure: L2 文件需要评定的列
    返回：对应的L0文件列（通道名），及不确定度分量文件名
    """
    if l2_col_measure == "Temperature(K)" or l2_col_measure == "TemperatureUncertainty(K)":
        return systemTypeL1ChannelInfoByL0FileType['Ry_DV']
    # if l2_col_measure in systemTypeL2ChannelInfo.keys():
    #     return systemTypeL2ChannelInfo[l2_col_measure]
    else:
        return ""


##############################################
# GUM算法
def algorithmGum(raw_data, conf):
    """
    :param raw_data: 待评定的L2 数据
    :param conf: 相关参数
    :return: 评定结果直接存库
    """
    print("GUM算法评定")
    if len(raw_data) <= 0:
        return {"code": -1, "msg": "GUM算法参数错误：未找到评定数据！"}

    # 参数
    height_range_value = Decimal(0.5)       # 取L0的高度范围 [value - height_range_value, value + height_range_value)

    # TODO 需要可设置(根据设备名称进行设置)
    UncertaintyComponentFilePath = "/root/measurementProject/system_conf/uncertainty_components_file/Lidar02"
    # WindowsTestUncertaintyComponentFilePaht = "D:/GBfiles/mycode/Workspace/MeasurementProject/test_data/uncertain_component_files/Lidar02"
    WindowsTestUncertaintyComponentFilePaht = "data/uncertain_component_files/Lidar02"

    file_path = ""
    if checkLinuxOrWindows() == 1:
        file_path = "%s" % UncertaintyComponentFilePath
    elif checkLinuxOrWindows() == 2:
        file_path = "%s" % WindowsTestUncertaintyComponentFilePaht
    else:
        return {"code": -1, "msg": "操作系统环境无法识别或不支持！"}

    print("测试GUM参数")
    print(conf)

    # 1、根据对应关系找到相应的L0级文件数据和不确定度分量文件数据
    # 评定的列包括：DenRay, DenAll, ZonalWind, MeridiWind, ZonalWindSM, MeridiWindSM, NaDen, TempAll,
    #               TempNa, TempRam, TempRay, ZonalWind, MeridiWind

    # 方法：根据站点Wuhan、设备名AWTL02（或文件投中Lidar II）、对应关系（见表3）查找Wuhan、AWTL02、532P类的L0级文件
    #       和Lidar02、RayD类的不确定度分量文件；
    head_id_l2 = raw_data[0]['HeadId']
    head_info = getHeadFullInfoByHeadIdV2(conf, head_id_l2)
    # ((49, '2022042515551021', 51, 1, 0, datetime.datetime(2019, 7, 1, 1, 0), datetime.datetime(2019, 7, 1, 2, 0),
    # 'D:/GBfiles/mycode/Workspace/MeasurementProject/test_data/test_data_220512_oneday24hoursTemp',
    # 'Wuhan_AWTL03_TUSM_L2_01H__20190101140000_V01.01DAT', 1, 'Wuhan', 6, 'Wuhan Lidar III', 104, 'Chinese Me
    # ridian Project for Space Environment Monitoring', -1, 0, datetime.datetime(2022, 5, 12, 22, 49, 30), None),)
    print(head_info)
    if head_info == "":
        print("未找到headId数据！")
        return 0

     # 根据实际数据重置DataType参数
    conf['DataType'] = head_info['DataType']

    # 找L0文件
    # 方法：根据文件头中起始时间DataStartTime: 2022-01-20T14:00:00Z、终止时间DataEndTime: 2022-01-20T15:00:00Z找到
    # 该时间范围的L0级文件，提供的L0级文件样例20220120140000到20220120145700都在该范围内，有58个文件（非固定值）。
    #data_type_l0 = getL0SystemTypeFromL2(head_info['DataType'])
    # 202309变更："T" --> ["Rm_DV", "Ry_DV", "Fe_DE", "Fe_DN"]
    data_type_l0 = getL0SystemTypeFromL22309(head_info['DataType'])
    print("L0级文件DataType: %s" % data_type_l0)
    if data_type_l0 == "":
        print("未找到L2探测体制 %s 对应的L0体制文件" % head_info['DataType'])
        return -1

    head_info_conf = {
        "ValueType": head_info['ValueType'],                # 测量值类型
        "DataStartTime": head_info['DataStartTime'],        # L2 数据的开始时间
        "DataEndTime": head_info['DataEndTime'],            # L2 数据的结束时间
        "StationId": head_info['StationId'],                # 站点ID
        "DeviceId": head_info['DeviceId'],                  # 设备ID
        "DataTypeL0": data_type_l0,                         # L2级文件类型对应L0级文件类型
    }

    # l0_head_id_list = getHeadIdByHeadInfo(head_info_conf, conf, 0, 0)
    l0_head_id_list = getHeadIdByHeadInfo2309(head_info_conf, conf, 0, 0)
    print("获取到对应的L0文件头部信息: %d" % len(l0_head_id_list))
    print(l0_head_id_list)

    l0_head_id_list_only = []
    for (head_id, value_type, start_time, end_time) in l0_head_id_list:
        l0_head_id_list_only.append(str(head_id))

    # 测试
    print(len(l0_head_id_list_only))
    print(l0_head_id_list_only)

    # 获取L0数据
    # 策略距离：对第一个高度30km处的TempRay进行不确定度评定，根据30km和下一个高度31km得到高度分辨率为1km，
    # （对于其他分辨率+- 0.5*分辨率）则L2级文件30km对应L0文件29.5km~30.5km（以L2级高度为中心的区间，前闭后开）的数据，
    # 根据对应关系表3，TempRay对应L0级数据RayVHS这一列
    if conf['Label'] is None or conf['Label'] == '':
        print("从数据类型获取探测体制..")
        print(conf['DataType'])
        conf['Label'] = systemDataType2Label[conf['DataType']]
    print("L2数据评定体制：%s" % conf['Label'])
    # label_l0 = getChannelInfo(conf['Label'])
    label_l0 = getChannelInfo2309(conf['Label'])
    print("L2对应的L0体制：")
    # 例如："TempRay":      {"l1_col_measure": ["RayVHS"], "measure_file_type": "RayT"},
    print(label_l0)
    if label_l0 == "":
        print("L2文件数据体制不支持：%s. 不进行不确定度评定！" % conf['Label'])
        return {"code": -1, "msg": "L2文件数据体制不支持：%s" % conf['Label']}

    # 多个通道
    print("获取L0的多个通道值..")
    label_l0_system_type_arr = label_l0['l1_col_measure']

    # 找不确定度分量文件
    # TODO 不同设备不确定度分量路径的获取
    uncertainty_component_filename = "Uncertainty_Lidar02_%s.txt" % label_l0['measure_file_type']
    uncertainty_component_file = "%s/%s" % (file_path, uncertainty_component_filename)

    print("不确定度分量文件：%s" % uncertainty_component_file)
    # 获取分量矩阵，u个数，c个数
    (uncertainty_component_data, u_count, c_count) = readUncertaintyComponentFile(uncertainty_component_file)
    # print(uncertainty_component_data)
    print("不确定度分量文件高度范围：")
    print(uncertainty_component_data.index)

    #######################################
    # 获取L2数据对应的L0文件数据，一次取所有高度
    max_height = float(raw_data[0]['Height'])
    min_height = float(raw_data[0]['Height'])
    for t in range(len(raw_data)):
        height = float(raw_data[t]['Height'])
        if height - max_height > EPSINON:
            max_height = height
        elif height - min_height < EPSINON:
            min_height = height
    print(min_height, max_height)
    height_start = min_height - float(height_range_value)
    height_end = max_height + float(height_range_value)
    system_type_list_str = '", "'.join(label_l0_system_type_arr)
    system_type_list_str = '"%s"' % system_type_list_str
    data_same_height_l0 = getL0RawDataByL2HeightRange(height_start, height_end, l0_head_id_list_only, system_type_list_str, 0)
    if data_same_height_l0 == -1:
        print("获取L2高度对应的L0文件数据失败！")
        return -1

    data_same_height_l0_dict = {}
    if data_same_height_l0 != "":
        for item in data_same_height_l0:
            height = item['Height']
            value = Decimal(item['Value'])
            label = item['Label']
            if height not in data_same_height_l0_dict.keys():
                data_same_height_l0_dict[height] = {}
            if label not in data_same_height_l0_dict[height].keys():
                data_same_height_l0_dict[height][label] = []
            data_same_height_l0_dict[height][label].append(value)

    # print(data_same_height_l0_dict)
    print(data_same_height_l0_dict.keys())
    for item in data_same_height_l0_dict.keys():
        print(data_same_height_l0_dict[item].keys())
        break

    # 不确定度计算
    value_arr = []
    # MCM计算参数值
    N_dict = {}          # 各通道求和获取L2同高度对应的L0原始数据.
    N_dict_count = {}    # 求和元素的个数

    print("原始数据：")
    # 测试
    print("原始数据：%d" % len(raw_data))
    if len(raw_data) > 2:
        print(raw_data[0:2])
    else:
        print(raw_data)
    # time.sleep(20)
    for t in range(len(raw_data)):
        # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
        # print("检查状态库表，是否停止程序..")
        if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
            return 0

        height = raw_data[t]['Height']
        value = Decimal(raw_data[t]['Value'])

        # 当前高度没有不确定度分量数据，跳过该高度的评定
        # L2/L0高度值：老数据是整数值，可直接和分量文件匹配高度；新数据为两位小数数据，无法直接和分量文件匹配高度。
        # 处理方法：对L2/L0高度取整，然后匹配分量文件的高度。
        print(height, int(height), value)
        #print(uncertainty_component_data.index)
        if int(height) not in uncertainty_component_data.index:
            print("当前高度没有不确定度分量数据，跳过该高度的评定!")
            continue

        height_start = height - height_range_value
        height_end = height + height_range_value
        print("[%.2f, %.2f)" % (height_start, height_end))

        # 2、计算各通道的光子噪声不确定度
        # 对对应的所用的L0级文件的RayVHS数据（对于30km处应有58*6个数据，对于31km处有58*10，数据量不固定）进行求和然后开平方，
        # 即为光子噪声不确定度。
        # 如果有多个通道需分开计算（通常最多3个），例如瑞利测风MeridiWindSL，则为

        N_arr = []          # 求和（光子数求和）
        u_N_arr = []        # 平方根（光子噪声不确定度）
        for system_type in label_l0_system_type_arr:
            # print("获取L2同高度对应的L0原始数据..L2_head_id=%d  height=%.2f" % (head_id_l2, height))
            if system_type not in N_dict.keys():
                N_dict[system_type] = []
                N_dict_count[system_type] = []

            print("通道： %s" % system_type)
            data_same_height_l0 = []
            for h in data_same_height_l0_dict.keys():
                if h - height_start >= EPSINON and h - height_end < EPSINON:
                    # print(height, height_start, height_end, h)
                    if system_type in data_same_height_l0_dict[h].keys():
                        data_same_height_l0.extend(data_same_height_l0_dict[h][system_type])

            # data_same_height_l0 = getL0RawDataByL2HeightRange(height_start, height_end, l0_head_id_list_only, system_type)
            # if data_same_height_l0 == -1:
            #     print("获取L2高度对应的L0文件数据失败！")
            #     return -1
            print(len(data_same_height_l0))
            #print(data_same_height_l0)
            # 测试
            print("输出L0同一高度数据(前10个)：")
            max_len = 10 if len(data_same_height_l0) >= 10 else len(data_same_height_l0)
            print(data_same_height_l0[0:max_len])
            if len(data_same_height_l0) <= 0:
                print("L0同一高度数据为0")
                continue

            # 测试
            # return

            N = np.sum(data_same_height_l0)
            u_N = math.sqrt(N)
            print(system_type, N, u_N)
            N_arr.append(N)
            u_N_arr.append(Decimal(u_N))
        print("测试-求和（光子数求和），平方根（光子噪声不确定度）：")
        print(N_arr)
        print(u_N_arr)
        if len(N_arr) == 0 or len(u_N_arr) == 0:
            print("当前高度无法计算GUM不确定度, head_id_l2 = %d, height = %d(取整：%d)" % (head_id_l2, height, int(height)))
            continue

        # 3、其余不确定度分量和所有灵敏系数获取
        # 读取Uncertainty_Lidar02_RayT.txt文件中，
        # 若找不到相应高度的直接不确定度分量u_xi和灵敏系数，
        # 则利用对高度进行线性插值获得该高度处的直接不确定度分量和灵敏系数。
        # 计算 u1-uN的值

        # TODO 检查不确定度分量文件中对应的高度是否存在（已移到前面）
        print(height, value)
        # print(uncertainty_component_data.loc[height, 'u_x1'])
        # print(uncertainty_component_data.loc[height, 'c11'])
        # print(uncertainty_component_data.loc[height,:])
        # col_name = "u_x%d" % (i+1)
        # print(uncertainty_component_data.loc[height, col_name])
        # print(uncertainty_component_data.iloc[height, i])
        # un_value = uncertainty_component_data.loc[height, col_name]

        # 获取指定高度行的不确定度分量灵敏系数数据
        # print(uncertainty_component_data.index.is_unique)
        # print(type(uncertainty_component_data.loc[height,:]))
        # if isinstance(uncertainty_component_data.loc[height,:], pd.Series):
        # print(uncertainty_component_data.index)

        # # 当前高度没有不确定度分量数据，跳过该高度的评定（已移到前面）
        # if height not in uncertainty_component_data.index:
        #     print("当前高度没有不确定度分量数据，跳过该高度的评定!")
        #     continue

        # 处理不确定度分量文件有重复高度的情况
        height_new = int(height)   #
        if isinstance(uncertainty_component_data.loc[height_new,:], pd.DataFrame):
            # 重复高度的情况，取第一行的不确定度分量数据
            un_vlue_vetor = uncertainty_component_data.loc[height_new,:].iloc[0]
        else:
            un_vlue_vetor = uncertainty_component_data.loc[height_new,:]

        # print(un_vlue_vetor)
        print(len(un_vlue_vetor))
        # print(un_vlue_vetor[0], un_vlue_vetor[12])

        # 使用 灵敏系数 计算ui 和 ci
        # 对于灵敏系数，若为单个值，则直接获取，若为矩阵如[a0,a1,a2]，则代表多项式，如下（N1为上一步计算的值）
        # 对于多通道，若矩阵[a0,a1,a2; b0,b1,b2]
        u_xi_arr = []          # 间接不确定度分量要素u
        un_N = []               # 光子噪声间接不确定度分量uT_N
        un_arr = []             # 保存不确定度分量u1-uN
        total_col_num = u_count + c_count
        print(u_count, c_count, uncertainty_component_file)
        # print(total_col_num)
        for i in range(total_col_num):
            # print("i: %d" % i)
            un_value = str(un_vlue_vetor[i])
            # print(i, un_value)
            # 如果为矩阵，计算ui 或 ci
            if '[' in un_value:
                ci = Decimal(0.0)
                if ';' not in un_value:
                    # 情况1-灵敏系数为矩阵-单通道：[1.0,1.0]
                    arr = un_value[1: len(un_value)-1].split(',')
                    #计算
                    for k in range(len(arr)):
                        ci += Decimal(arr[k]) * N_arr[0] ** k
                else:
                    # 情况2-灵敏系数为矩阵-多通道：[1.0,1.0;1.0,1.0]
                    channel_arr = un_value[1: len(un_value)-1].split(';')
                    for j in range(len(channel_arr)):
                        arr = channel_arr[j].split(',')
                        # 计算
                        for k in range(len(arr)):
                            ci += Decimal(arr[k]) * N_arr[j] ** k
            else:
                # 情况3-灵敏系数为单个值：1.0
                #u_xi_arr.append(Decimal(un_value))
                ci = Decimal(un_value)

            # 计算分量
            if i < u_count:
                # 保存
                u_xi_arr.append(ci)
            elif u_count <= i < 2 * u_count:
                # 计算不确定度分量u1-uN（间接不确定度分量的计算）
                un_component = ci * u_xi_arr[i - u_count]
                un_arr.append(un_component)
            else:
                # 计算光子噪声间接不确定度分量uT_N（数量和通道数对应）
                uT_N = ci * u_N_arr[i - 2 * u_count]
                un_N.append(uT_N)

        print("测试间接不确定度分量u1-uN，及光子噪声间接不确定度分量uT_N：")
        print(un_arr)
        print(un_N)

        # 4、计算合成不确定度（标准不确定度）
        uT = math.sqrt(square_of_sum(un_arr) + square_of_sum(un_N))
        #gumResult = rand

        # 5、计算扩展不确定度
        print(conf['Factor'])
        UT = uT * conf['Factor']

        print("不确定度结果：")
        print(uT)
        print(UT)
        print(raw_data[t])

        # 计算结果填充到原数据结构中
        raw_data[t]['GumResult'] = str(uT)
        raw_data[t]['GumResultExtended'] = str(UT)
        raw_data[t]['GumParameterK'] = conf['Factor']
        raw_data[t]['BatchId'] = conf['TaskId']
        # 其他填充值
        if 'DataStartTime' in raw_data[t].keys():
            if raw_data[t]['DataStartTime'] is None:
                raw_data[t]['DataStartTime'] = head_info['DataStartTime']
            if raw_data[t]['DataEndTime'] is None:
                raw_data[t]['DataEndTime'] = head_info['DataEndTime']
        if 'DefaultValue' in raw_data[t].keys():
            if raw_data[t]['DefaultValue'] is None:
                raw_data[t]['DefaultValue'] = 0.0

        # 填充u1-uN
        for index in range(len(un_arr)):
            key = "U%d" % (index + 1)
            raw_data[t][key] = un_arr[index]
        # 填充通道1-3光子噪声不确定度分量
        for index in range(len(un_N)):
            key = "UTN%d" % (index + 1)
            raw_data[t][key] = un_N[index]
        # 其他填充初始值
        if raw_data[t]['McmParameterExpTimes'] is None:
            raw_data[t]['McmParameterExpTimes'] = 0

        print(raw_data[t])

        value_arr.append(raw_data[t])
        # 测试
        #break

    # 保存到数据库
    print("存储评定结果..HeadId = %d (l2: %d), 评定数量：%d" % (head_id, head_id_l2, len(value_arr)))
    # 根据headid先删除之前评定的结果
    delete_pre_data(conf['DbSaveTable'], [str(head_id_l2)])
    # time.sleep(30)
    saveAlgorithmResult(value_arr, conf)
    # time.sleep(30)
    # 更新不确定度分量文件到HeadId信息
    print("存储评定不确定度分量文件名称..")
    db_head_table = db_data_head_table if conf['SaveFlag'] == 1 else db_temporary_data_head_table
    print(db_data_head_table, head_id_l2, uncertainty_component_filename)
    updateHeadTableUncertaintyComponentFileByHeadId(db_head_table, head_id_l2, uncertainty_component_filename)

    return len(value_arr)


def saveAlgorithmResult(data_json_list, conf):
    # 获取表字段db_cols,和值列表 value_arr
    db_cols = []
    value_arr = []
    if len(data_json_list)>0:
        for k in data_json_list[0].keys():
            db_cols.append(k)

    for data_json in data_json_list:
        tmp_list = []
        for k in data_json.keys():
            tmp_list.append('"%s"' % str(data_json[k]))
            # if k == "Value":
            #     tmp_list.append('"%.8f"' % data_json[k])
            # else:
            #     tmp_list.append('"%s"' % str(data_json[k]))
        data_str = ','.join(tmp_list)
        value_arr.append(data_str)

    # 存库
    if conf['SaveFlag'] == 1:
        update_data_into_db(db_raw_l2_precessed_table, db_cols, value_arr, 1)
    else:
        update_data_into_db(db_temporary_data_table_l2, db_cols, value_arr, 1)


def saveAlgorithmResult2309(data_json_list, conf):
    # 获取表字段db_cols,和值列表 value_arr
    db_cols = []
    value_arr = []
    if len(data_json_list)>0:
        for k in data_json_list[0].keys():
            db_cols.append(k)

    for data_json in data_json_list:
        tmp_list = []
        for k in data_json.keys():
            tmp_list.append('"%s"' % str(data_json[k]))
            # if k == "Value":
            #     tmp_list.append('"%.8f"' % data_json[k])
            # else:
            #     tmp_list.append('"%s"' % str(data_json[k]))
        data_str = ','.join(tmp_list)
        value_arr.append(data_str)

    # 存库
    if conf['SaveFlag'] == 1:
        param_db_table = db_raw_l2_precessed_table
    else:
        param_db_table = db_temporary_data_table_l2

    update_data_into_db_for_gum_mcm(param_db_table, db_cols, value_arr, conf['OnUpdateCondition'], 1)


##############################################
# MCM算法
# def algorithmMcm_bak(raw_data, conf):
#     """
#     :param raw_data: 待评定的L2 数据
#     :param conf: 相关参数
#     :return: 评定结果直接存库
#     """
#     print("MCM算法评定")
#     if len(raw_data) <= 0:
#         return {"code": -1, "msg": "MCM算法参数错误：未找到评定数据！"}
#
#     # 参数
#     height_range_value = Decimal(0.5)       # 取L0的高度范围 [value - height_range_value, value + height_range_value)
#
#     print("测试MCM参数")
#     print(conf)
#
#     # 1、根据对应关系找到相应的L0级文件数据和不确定度分量文件数据
#     # 评定的列包括：DenRay, DenAll, ZonalWind, MeridiWind, ZonalWindSM, MeridiWindSM, NaDen, TempAll,
#     #               TempNa, TempRam, TempRay, ZonalWind, MeridiWind
#
#     # 方法：根据站点Wuhan、设备名AWTL02（或文件投中Lidar II）、对应关系（见表3）查找Wuhan、AWTL02、532P类的L0级文件
#     #       和Lidar02、RayD类的不确定度分量文件；
#     head_id = raw_data[0]['HeadId']
#     head_info = getHeadFullInfoByHeadId(head_id)
#     print(head_info)
#     if head_info == "":
#         print("未找到headId数据！")
#         return 0
#
#     # 找L0文件
#     # 方法：根据文件头中起始时间DataStartTime: 2022-01-20T14:00:00Z、终止时间DataEndTime: 2022-01-20T15:00:00Z找到
#     # 该时间范围的L0级文件，提供的L0级文件样例20220120140000到20220120145700都在该范围内，有58个文件（非固定值）。
#     data_type_l0 = getL0SystemTypeFromL2(head_info['DataType'])
#     print("L0级文件DataType: %s" % data_type_l0)
#     if data_type_l0 == "":
#         print("未找到L2探测体制 %s 对应的L0体制文件" % head_info['DataType'])
#         return -1
#
#     head_info_conf = {
#         "ValueType": head_info['ValueType'],                # 测量值类型
#         "DataStartTime": head_info['DataStartTime'],        # L2 数据的开始时间
#         "DataEndTime": head_info['DataEndTime'],            # L2 数据的结束时间
#         "StationId": head_info['StationId'],                # 站点ID
#         "DeviceId": head_info['DeviceId'],                  # 设备ID
#         "DataTypeL0": data_type_l0,                         # L2级文件类型对应L0级文件类型
#     }
#
#     l0_head_id_list = getHeadIdByHeadInfo(head_info_conf, conf, 0)
#     print("获取到对应的L0文件头部信息: %d" % len(l0_head_id_list))
#     print(l0_head_id_list)
#
#     l0_head_id_list_only = []
#     for (head_id, value_type, start_time, end_time) in l0_head_id_list:
#         l0_head_id_list_only.append(str(head_id))
#
#     # 获取L0数据（同GUM算法）
#     # 策略距离：对第一个高度30km处的TempRay进行不确定度评定，根据30km和下一个高度31km得到高度分辨率为1km，
#     # （对于其他分辨率+- 0.5*分辨率）则L2级文件30km对应L0文件29.5km~30.5km（以L2级高度为中心的区间，前闭后开）的数据，
#     # 根据对应关系表3，TempRay对应L0级数据RayVHS这一列
#     print("L2数据评定体制：%s" % conf['Label'])
#     label_l0 = getChannelInfo(conf['Label'])
#     print("L2对应的L0体制：")
#     # 例如："TempRay":      {"l1_col_measure": ["RayVHS"], "measure_file_type": "RayT"},
#     print(label_l0)
#     if label_l0 == "":
#         print("L2文件数据体制不支持：%s. 不进行不确定度评定！" % conf['Label'])
#         return {"code": -1, "msg": "L2文件数据体制不支持：%s" % conf['Label']}
#
#     # 多个通道
#     print("获取L0的多个通道值..")
#     label_l0_system_type_arr = label_l0['l1_col_measure']
#     #print(label_l0_system_type_arr)
#
#     # 以下开始为MCM算法专属流程
#     # # 找不确定度分量文件
#     # # 不同设备不确定度分量路径的获取
#     # uncertainty_component_file = "%s/Uncertainty_Lidar02_%s.txt" % (file_path, label_l0['measure_file_type'])
#     # # 获取分量矩阵，u个数，c个数
#     # (uncertainty_component_data, u_count, c_count) = readUncertaintyComponentFile(uncertainty_component_file)
#     # # print(uncertainty_component_data)
#
#     # 读入MCM参数：从csv文件中获取设备参数
#     parameter_list = getDeviceParameterBySystemTypeForMcmAlgorithm(conf['DeviceLidar02SystemTypeInfoForMcm'], conf['DataType'])
#     print(parameter_list)
#
#     # 试验次数
#     exp_times = conf['McmParameterExpTimes']
#
#     # 不确定度计算
#     value_arr = []
#     for t in range(len(raw_data)):
#         # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
#         print("检查状态库表，是否停止程序..")
#         if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
#             return 0
#
#         height = raw_data[t]['Height']
#         value = Decimal(raw_data[t]['Value'])
#
#         height_start = height - height_range_value
#         height_end = height + height_range_value
#
#         N_arr = []          # 求和获取L2同高度对应的L0原始数据.
#         N_arr_count = []    # 求和元素的个数
#         for system_type in label_l0_system_type_arr:
#             data_same_height_l0 = getL0RawDataByL2HeightRange(height_start, height_end, l0_head_id_list_only, system_type)
#             if data_same_height_l0 == -1:
#                 print("获取L2高度对应的L0文件数据失败！")
#                 return -1
#             # print(data_same_height_l0)
#             # 测试
#             max_len = 10 if len(data_same_height_l0) >= 10 else len(data_same_height_l0)
#             print(data_same_height_l0[0:max_len])
#
#             if len(data_same_height_l0) <= 0:
#                 continue
#             N = np.sum(data_same_height_l0)
#             N_arr.append(N)
#             N_arr_count.append(len(data_same_height_l0))
#         print("测试-求和（光子数求和）：")
#         print(N_arr)
#         if len(N_arr) == 0:
#             print("当前高度无法计算MCM不确定度, head_id_l2 = %d, height = %d" % (head_id, height))
#             continue
#
#         # 针对不同体制，计算分布
#         mcm_result = 0.0
#         if conf['DataType'] == "TSVP":
#             mcm_result = mcmAlgorithmForTSVP(N_arr, N_arr_count, parameter_list, exp_times)
#
#         print(raw_data[t])
#
#         # 计算结果填充到原数据结构中
#         raw_data[t]['McmResult'] = mcm_result
#         raw_data[t]['McmParameterP'] = conf['McmParameterP']
#         raw_data[t]['McmParameterExpTimes'] = conf['McmParameterExpTimes']
#         raw_data[t]['BatchId'] = conf['TaskId']
#
#         print(raw_data[t])
#
#         value_arr.append(raw_data[t])
#
#         # 测试
#         # break
#
#     # 保存到数据库
#     saveAlgorithmResult(value_arr, conf)
#
#     return 0


# MCM 不同体制计算方法不一样
def mcmAlgorithmForTSVP(N_arr, N_arr_count, parameter_list, exp_times):
    # 以[各通道]为均值，进行泊松分布模拟生成10^6次方样本
    N_Ram_z = []
    #print(len(N_arr))
    for i in range(len(N_arr)):
        N_Ram_tmp = np.random.poisson(lam=N_arr[i], size=exp_times)
        N_Ram_z.append(N_Ram_tmp)

    # 以[探测器量子效率]为均值，为[探测器量子效率波动]标准差，进行正态分布模拟生成10^6次方样本；
    mu = getValueByParameterName(parameter_list, "探测器量子效率")             # 均值
    sigma = getValueByParameterName(parameter_list, "探测器量子效率波动度")      # 标准差
    print(mu, sigma)
    eta = np.random.normal(loc=str_to_float(mu), scale=str_to_float(sigma), size=exp_times)

    # 以[死区时间]为均值，[死区时间波动]为标准差，进行正态分布模拟生成10^6次方样本；
    mu = getValueByParameterName(parameter_list, "探测器死区时间")            # 均值
    sigma = getValueByParameterName(parameter_list, "探测器死区时间波动度")     # 标准差
    print(mu, sigma)
    tau = np.random.normal(loc=str_to_float(mu), scale=str_to_float(sigma), size=exp_times)

    # 以[探测器暗噪声]为均值，[暗噪声波动]为标准差，进行正态分布模拟生成10^6次方样本；
    mu = getValueByParameterName(parameter_list, "探测器暗噪声")         # 均值
    sigma = getValueByParameterName(parameter_list, "探测器暗噪声波动度")        # 标准差
    print(mu, sigma)
    B1_z = np.random.normal(loc=str_to_float(mu), scale=str_to_float(sigma), size=exp_times)

    N_T_Z = []
    for i in range(len(N_Ram_z)):
        delta_z = str_to_float(getValueByParameterName(parameter_list, "L0级高度分辨率"))
        delta_t = str_to_float(getValueByParameterName(parameter_list, "L2级积分时间"))
        f = str_to_float(getValueByParameterName(parameter_list, "激光器重频"))
        L = delta_t * f
        print(delta_z, delta_t, f, L)
        n = N_arr_count[i]   # L2对应的L0高度光子数数量（求和的元素数量）

        Nc_z = N_Ram_z[i] / (1 - (tau * LIGHTSPEED * N_Ram_z[i] / (2 * delta_z * L)))
        Nb_z = Nc_z - n * B1_z
        Np_z = Nb_z / eta

        print(Np_z)
        N_T_Z.append(Np_z)

    a = str_to_float(getValueByParameterName(parameter_list, "拟合系数a"))
    b = str_to_float(getValueByParameterName(parameter_list, "拟合系数b"))
    c = str_to_float(getValueByParameterName(parameter_list, "拟合系数c"))
    print(a, b, c)
    Q_p = N_T_Z[1] / N_T_Z[0]
    print(Q_p)
    #print(np.log(Q_p))
    # f_x = np.poly1d(np.log(Q_p)-c, -b, -a)
    # A = np.log(Q_p)
    # vector = []
    # for i in range(len(A)):
    #     f_x = np.poly1d(A[i]-c, -b, -a)
    #     print(f_x, f_x.r)
    #     # arr = f_x.r
    #     break
    # T = (-b - np.sqrt(b**2 - 4 * (a - np.log(Q_p) * c))) / 2 * (a - np.log(Q_p))
    T = 1 / (c * ((np.log(Q_p))**2) + b * np.log(Q_p) + a)
    print(T)
    T_std = np.std(T)
    print(T_std)

    return T_std


def mcmAlgorithmForDUSM(N_arr, parameter_list, exp_times):
    return 0


def mcmAlgorithmForTUSM(N_arr, parameter_list, exp_times):
    return 0


def mcmAlgorithmForHWSLandHWSM(N_arr, parameter_list, exp_times):
    return 0


def mcmAlgorithmForTMSLandHWSL(N_arr, parameter_list, exp_times):
    return 0


def computeExperimentTimes(p):
    """
    蒙特卡洛试验次数和置信概率关系
    p：置信概率
    return：实验次数
    计算公式：M = 10^4/(1-p)  （四舍五入取整）
    """
    if p == "":
        return -1

    M = round(10**4 / (1 - str_to_float(p)))
    print("置信概率：%s, 实验次数：%d" % (p, M))
    return M


def algorithmMcm(raw_data, conf):
    """
    :param raw_data: 待评定的L2 数据
    :param conf: 相关参数
    :return: 评定结果直接存库
    """
    print("MCM算法评定")
    if len(raw_data) <= 0:
        return {"code": -1, "msg": "MCM算法参数错误：未找到评定数据！"}

    # 参数
    height_range_value = Decimal(0.5)       # 取L0的高度范围 [value - height_range_value, value + height_range_value)

    # 1、根据对应关系找到相应的L0级文件数据和不确定度分量文件数据
    # 评定的列包括：DenRay, DenAll, ZonalWind, MeridiWind, ZonalWindSM, MeridiWindSM, NaDen, TempAll,
    #               TempNa, TempRam, TempRay, ZonalWind, MeridiWind

    # 方法：根据站点Wuhan、设备名AWTL02（或文件投中Lidar II）、对应关系（见表3）查找Wuhan、AWTL02、532P类的L0级文件
    #       和Lidar02、RayD类的不确定度分量文件；
    head_id = raw_data[0]['HeadId']
    head_info = getHeadFullInfoByHeadIdV2(conf, head_id)
    print(head_info)
    if head_info == "":
        print("未找到headId数据！")
        return 0

    # 根据实际数据重置DataType参数
    conf['DataType'] = head_info['DataType']

    print("测试MCM参数")
    print(conf)

    # 找L0文件
    # 方法：根据文件头中起始时间DataStartTime: 2022-01-20T14:00:00Z、终止时间DataEndTime: 2022-01-20T15:00:00Z找到
    # 该时间范围的L0级文件，提供的L0级文件样例20220120140000到20220120145700都在该范围内，有58个文件（非固定值）。
    # data_type_l0 = getL0SystemTypeFromL2(head_info['DataType'])
    # 202309变更："T" --> ["Rm_DV", "Ry_DV", "Fe_DE", "Fe_DN"]
    data_type_l0 = getL0SystemTypeFromL22309(head_info['DataType'])
    print("L0级文件DataType: %s" % data_type_l0)
    if data_type_l0 == "":
        print("未找到L2探测体制 %s 对应的L0体制文件" % head_info['DataType'])
        return -1

    head_info_conf = {
        "ValueType": head_info['ValueType'],                # 测量值类型
        "DataStartTime": head_info['DataStartTime'],        # L2 数据的开始时间
        "DataEndTime": head_info['DataEndTime'],            # L2 数据的结束时间
        "StationId": head_info['StationId'],                # 站点ID
        "DeviceId": head_info['DeviceId'],                  # 设备ID
        "DataTypeL0": data_type_l0,                         # L2级文件类型对应L0级文件类型
    }

    # l0_head_id_list = getHeadIdByHeadInfo(head_info_conf, conf, 0, 0)
    l0_head_id_list = getHeadIdByHeadInfo2309(head_info_conf, conf, 0, 0)
    print("获取到对应的L0文件头部信息: %d" % len(l0_head_id_list))
    print(l0_head_id_list)

    l0_head_id_list_only = []
    for (head_id, value_type, start_time, end_time) in l0_head_id_list:
        l0_head_id_list_only.append(str(head_id))

    # 获取L0数据（同GUM算法）
    # 策略距离：对第一个高度30km处的TempRay进行不确定度评定，根据30km和下一个高度31km得到高度分辨率为1km，
    # （对于其他分辨率+- 0.5*分辨率）则L2级文件30km对应L0文件29.5km~30.5km（以L2级高度为中心的区间，前闭后开）的数据，
    # 根据对应关系表3，TempRay对应L0级数据RayVHS这一列
    if conf['Label'] is None or conf['Label'] == '':
        print("从数据类型获取探测体制..")
        print(conf['DataType'])
        conf['Label'] = systemDataType2Label[conf['DataType']]
    print("L2数据评定体制：%s" % conf['Label'])
    # label_l0 = getChannelInfo(conf['Label'])
    label_l0 = getChannelInfo2309(conf['Label'])
    print("L2对应的L0体制：")
    # 例如："TempRay":      {"l1_col_measure": ["RayVHS"], "measure_file_type": "RayT"},
    print(label_l0)
    if label_l0 == "":
        print("L2文件数据体制不支持：%s. 不进行不确定度评定！" % conf['Label'])
        return {"code": -1, "msg": "L2文件数据体制不支持：%s" % conf['Label']}

    # 多个通道
    print("获取L0的多个通道值..")
    label_l0_system_type_arr = label_l0['l1_col_measure']
    # print(label_l0_system_type_arr)

    # 以下开始为MCM算法专属流程
    # # 找不确定度分量文件
    # # 不同设备不确定度分量路径的获取
    # uncertainty_component_file = "%s/Uncertainty_Lidar02_%s.txt" % (file_path, label_l0['measure_file_type'])
    # # 获取分量矩阵，u个数，c个数
    # (uncertainty_component_data, u_count, c_count) = readUncertaintyComponentFile(uncertainty_component_file)
    # # print(uncertainty_component_data)

    # 读入MCM参数：从csv文件中获取设备参数
    parameter_list = getDeviceParameterBySystemTypeForMcmAlgorithm(conf['DeviceLidar02SystemTypeInfoForMcm'], conf['DataType'])
    print(parameter_list)
    print(len(parameter_list))

    # 获取L2数据对应的L0文件，一次取所有高度
    max_height = float(raw_data[0]['Height'])
    min_height = float(raw_data[0]['Height'])
    for t in range(len(raw_data)):
        height = float(raw_data[t]['Height'])
        if height - max_height > EPSINON:
            max_height = height
        elif height - min_height < EPSINON:
            min_height = height
    print(min_height, max_height)
    height_start = min_height - float(height_range_value)
    height_end = max_height + float(height_range_value)
    system_type_list_str = '", "'.join(label_l0_system_type_arr)
    system_type_list_str = '"%s"' % system_type_list_str
    data_same_height_l0 = getL0RawDataByL2HeightRange(height_start, height_end, l0_head_id_list_only, system_type_list_str, 0)
    if data_same_height_l0 == -1:
        print("获取L2高度对应的L0文件数据失败！")
        return -1

    data_same_height_l0_dict = {}
    if data_same_height_l0 != "":
        for item in data_same_height_l0:
            height = item['Height']
            value = Decimal(item['Value'])
            label = item['Label']
            if height not in data_same_height_l0_dict.keys():
                data_same_height_l0_dict[height] = {}
            if label not in data_same_height_l0_dict[height].keys():
                data_same_height_l0_dict[height][label] = []
            data_same_height_l0_dict[height][label].append(value)

    # print(data_same_height_l0_dict)
    # print(data_same_height_l0_dict.keys())
    # for item in data_same_height_l0_dict.keys():
    #     print(data_same_height_l0_dict[item].keys())

    # 不确定度计算
    value_arr = []
    # MCM计算参数值
    N_dict = {}          # 各通道求和获取L2同高度对应的L0原始数据.
    N_dict_count = {}    # 求和元素的个数
    Height_dict = []     # 探测高度向量
    raw_data_perform_alg = []  # 参与评定的高度（某些高度没有L0数据，无法评定）

    print("原始数据：", raw_data)
    for t in range(len(raw_data)):
        # 检查算法是否请求停止（前台界面用户点击“暂停”按钮的响应）
        # print("检查状态库表，是否停止程序..")
        if isTaskStoped(conf['TaskId'], conf['TaskType']) == 1:
            return 0

        height = raw_data[t]['Height']
        value = Decimal(raw_data[t]['Value'])
        print("高度：%.2f" % height)

        height_start = height - height_range_value
        height_end = height + height_range_value

        for system_type in label_l0_system_type_arr:
            if system_type not in N_dict.keys():
                N_dict[system_type] = []
                N_dict_count[system_type] = []

            print("通道： %s" % system_type)
            data_same_height_l0 = []
            for h in data_same_height_l0_dict.keys():
                if h - height_start >= EPSINON and h - height_end < EPSINON:
                    # print(height, height_start, height_end, h)
                    if system_type in data_same_height_l0_dict[h].keys():
                        data_same_height_l0.extend(data_same_height_l0_dict[h][system_type])

            # data_same_height_l0 = getL0RawDataByL2HeightRange(height_start, height_end, l0_head_id_list_only, system_type)
            # if data_same_height_l0 == -1:
            #     print("获取L2高度对应的L0文件数据失败！")
            #     return -1
            print(len(data_same_height_l0))
            # print(data_same_height_l0)
            # 测试
            print("输出L0同一高度数据(前10个)：")
            max_len = 10 if len(data_same_height_l0) >= 10 else len(data_same_height_l0)
            print(data_same_height_l0[0:max_len])

            if len(data_same_height_l0) <= 0:
                print("无法评定该高度不确定度： %.2f" % head_id)
                continue

            N = np.sum(data_same_height_l0)
            N_dict[system_type].append(float(N))
            N_dict_count[system_type].append(len(data_same_height_l0))

        # 参与评定的高度（某些高度没有L0数据，无法评定）
        if len(data_same_height_l0) > 0:
            Height_dict.append(float(height))
            raw_data_perform_alg.append(raw_data[t])

        print("测试-求和（光子数求和）：")
        print(N_dict)
        print(N_dict_count)
        # print(len(Height_dict))
        # print(Height_dict)
        for k in N_dict.keys():
            if len(N_dict[k]) == 0:
                print("当前L2文件对应的L0文件值求和为空！请检查原始数据！")
                return 0

    # 填充MCM算法参数
    print(conf['DataType'])
    conf['Height'] = Height_dict
    # 202309针对新数据的体制
    if conf['DataType'] == "T" or conf['DataType'] == "D":
        conf['RayVDP'] = N_dict['RayVDP']
        conf['RayVNP'] = N_dict['RayVNP']
    # elif conf['DataType'] == "W1" or conf['DataType'] == "W2":
    #     conf['xxx']
    #     TODO

    #以下为老测试数据的体制，以后不再使用
    elif conf['DataType'] == "TSVP":
        conf['RR1'] = N_dict['RR1']
        conf['RR2'] = N_dict['RR2']
    elif conf['DataType'] == "DUSM":
        conf['RayVLS'] = N_dict['RayVLS']
    elif conf['DataType'] == "HWLS":
        # 纬向风速
        if conf['Label'] == "ZonalWindSL":
            conf['channel1'] = N_dict['RayWLS']
            conf['channel2'] = N_dict['RayWLR']
        # 径向风速
        elif conf['Label'] == "MeridiWindSL":
            conf['channel1'] = N_dict['RayNLS']
            conf['channel2'] = N_dict['RayNLR']
    elif conf['DataType'] == "HWSM":
        # 纬向风速
        if conf['Label'] == "ZonalWindSM":
            conf['channel1'] = N_dict['RayWHS']
            conf['channel2'] = N_dict['RayWHR']
        # 径向风速
        elif conf['Label'] == "MeridiWindSM":
            conf['channel1'] = N_dict['RayNHS']
            conf['channel2'] = N_dict['RayNHR']
    elif conf['DataType'] == "TUSM":
        conf['RayVHS'] = N_dict['RayVHS']
    elif conf['DataType'] == "HWSL":
        # 纬向风速
        if conf['Label'] == "ZonalWind":
            conf['channel1'] = N_dict['NaWF0']
            conf['channel2'] = N_dict['NaWF1']
            conf['channel3'] = N_dict['NaWF2']
        # 径向风速
        elif conf['Label'] == "MeridiWind":
            conf['channel1'] = N_dict['NaNF0']
            conf['channel2'] = N_dict['NaNF1']
            conf['channel3'] = N_dict['NaNF2']
    elif conf['DataType'] == "TMSL":
        conf['NaVF0'] = N_dict['NaVF0']
        conf['NaVF1'] = N_dict['NaVF1']
        conf['NaVF2'] = N_dict['NaVF2']

    print(conf)
    # return

    # 调用MCM算法
    print("当前文件MCM不确定度, head_id_l2 = %d" % head_id)
    mcm_result_vec = performMcmAlgorithm(conf)
    print("MCM计算结果（文件headid = %d）：" % head_id)
    print(mcm_result_vec)

    print(len(mcm_result_vec))
    print(len(raw_data_perform_alg))
    print(len(Height_dict))
    # 收集计算结果
    for t in range(len(raw_data_perform_alg)):
        # 计算结果填充到原数据结构中
        raw_data_perform_alg[t]['McmResult'] = mcm_result_vec[t]
        raw_data_perform_alg[t]['McmParameterP'] = conf['McmParameterP']
        raw_data_perform_alg[t]['McmParameterExpTimes'] = conf['McmParameterExpTimes']
        raw_data_perform_alg[t]['BatchId'] = conf['TaskId']
        # 其他填充值
        if 'DataStartTime' in raw_data_perform_alg[t].keys():
            if raw_data_perform_alg[t]['DataStartTime'] is None:
                raw_data_perform_alg[t]['DataStartTime'] = head_info['DataStartTime']
            if raw_data_perform_alg[t]['DataEndTime'] is None:
                raw_data_perform_alg[t]['DataEndTime'] = head_info['DataEndTime']
        if 'DefaultValue' in raw_data_perform_alg[t].keys():
            if raw_data_perform_alg[t]['DefaultValue'] is None:
                raw_data_perform_alg[t]['DefaultValue'] = 0.0

        #print(raw_data[t])

        value_arr.append(raw_data_perform_alg[t])

        # 测试
        # break

    # 保存到数据库
    # 根据headid先删除之前评定的结果
    delete_pre_data(conf['DbSaveTable'], [str(head_id)])
    saveAlgorithmResult(value_arr, conf)

    return len(value_arr)


def performMcmAlgorithm(conf):
    data_type = conf['DataType']

    parameter_list = getDeviceParameterBySystemTypeForMcmAlgorithm(conf['DeviceLidar02SystemTypeInfoForMcm'], data_type)

    if data_type == "T" or data_type == "D":
        param = {
            'n_MCM': conf['McmParameterExpTimes'],        # MCM试验次数
            'N_sum1': conf['RayVDP'],                        # 通道1(RayVNP)L0级数据（求和后）
            'N_sum2': conf['RayVNP'],                        # 通道2(RayVNP)L0级数据（求和后）
            'dead_time': getValueByParameterName(parameter_list, "探测器死区时间"),
            'uncertain_dead_time': getValueByParameterName(parameter_list, "探测器死区时间波动度"),
            'dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声"),
            'uncertain_dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声波动度"),
            'quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率"),
            'uncertain_quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率波动度"),
            'pulse_repeat': getValueByParameterName(parameter_list, "激光器重频"),
            'pulse_time': getValueByParameterName(parameter_list, "L2级积分时间"),
            'poly_coef': getListByParameterName(parameter_list, "拟合系数"),
            'channel_H': 1,       # 高阶通道编号
            'channel_L': 2,       # 低阶通道编号
        }
        print("MCM计算参数（T）：")
        print(param)
        mcm_result_vec = inversion_Raman(param['n_MCM'], param['N_sum1'], param['N_sum2'], param['dead_time'],
                                         param['uncertain_dead_time'], param['dark_noise'], param['uncertain_dark_noise'],
                                         param['quantum_efficiency'], param['uncertain_quantum_efficiency'], param['pulse_repeat'],
                                         param['pulse_time'], param['poly_coef'], param['channel_H'], param['channel_L'])
    # 拉曼测温（适用于TSVP）
    elif data_type == "TSVP":
        param = {
            'n_MCM': conf['McmParameterExpTimes'],        # MCM试验次数
            'N_sum1': conf['RR1'],                        # 通道1(RR1)L0级数据（求和后）
            'N_sum2': conf['RR2'],                        # 通道2(RR2)L0级数据（求和后）
            'dead_time': getValueByParameterName(parameter_list, "探测器死区时间"),
            'uncertain_dead_time': getValueByParameterName(parameter_list, "探测器死区时间波动度"),
            'dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声"),
            'uncertain_dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声波动度"),
            'quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率"),
            'uncertain_quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率波动度"),
            'pulse_repeat': getValueByParameterName(parameter_list, "激光器重频"),
            'pulse_time': getValueByParameterName(parameter_list, "L2级积分时间"),
            'poly_coef': getListByParameterName(parameter_list, "拟合系数"),
            'channel_H': 1,       # 高阶通道编号
            'channel_L': 2,       # 低阶通道编号
        }
        print("MCM计算参数（TSVP）：")
        print(param)
        mcm_result_vec = inversion_Raman(param['n_MCM'], param['N_sum1'], param['N_sum2'], param['dead_time'],
                               param['uncertain_dead_time'], param['dark_noise'], param['uncertain_dark_noise'],
                               param['quantum_efficiency'], param['uncertain_quantum_efficiency'], param['pulse_repeat'],
                               param['pulse_time'], param['poly_coef'], param['channel_H'], param['channel_L'])

    # 瑞利测密（适用于DUSM）
    elif data_type == "DUSM":
        param = {
            'n_MCM': conf['McmParameterExpTimes'],        # MCM试验次数
            'S_time_sum': conf['RayVLS'],                 # 探测文件各高度L0级数据RayVHS（求和后），数组[1e8,1e6,……,90]
            'Altitude_final': conf['Height'],             # L2级探测文件数据高度，如[20,21,……,80]
            'dead_time': getValueByParameterName(parameter_list, "探测器死区时间"),                # 探测器死区时间
            'uncertain_dead_time': getValueByParameterName(parameter_list, "探测器死区时间波动度"),# 探测器死区时间波动度
            'dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声"),                 # 探测器暗噪声
            'uncertain_dark_noise':  getValueByParameterName(parameter_list, "探测器暗噪声波动度"),# 探测器暗噪声波动度
            'quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率"),       # 探测器量子效率
            'uncertain_quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率波动度"),   # 探测器量子效率波动度
            'pulse_repeat': getValueByParameterName(parameter_list, "激光器重频"),                 # 激光器重频
            'pulse_time': getValueByParameterName(parameter_list, "L2级积分时间"),                 # L2级积分时间
            'altitudeSysten_MCM': getValueByParameterName(parameter_list, "系统所在高度"),         # 系统所在高度
            'altitude_inf': getValueByParameterName(parameter_list, "参考密度所在高度"),           # 参考密度所在高度
            'density_inf': getValueByParameterName(parameter_list, "参考密度"),                    # 参考密度
            'uncertain_densityRef': getValueByParameterName(parameter_list, "参考密度波动度"),     # 参考密度波动度
        }
        print("MCM计算参数（DUSM）：")
        print(param)

        mcm_result_vec = inversion_Rayleigh_density(param['n_MCM'],param['S_time_sum'],param['Altitude_final'],
                        param['dead_time'],param['uncertain_dead_time'],param['dark_noise'],
                        param['uncertain_dark_noise'],param['quantum_efficiency'],
                        param['uncertain_quantum_efficiency'],param['pulse_repeat'],param['pulse_time'],
                        param['altitudeSysten_MCM'],param['altitude_inf'],
                        param['density_inf'],param['uncertain_densityRef'])

    # 瑞利测风（适用于HWLS、HWSM，包括经向风速Meridi和纬向风速Zonal）
    elif data_type == "HWLS" or data_type == "HWSM":
        param = {
            'n_MCM': conf['McmParameterExpTimes'],  # MCM试验次数
            'N_sum1': conf['channel1'],                 # 通道1的L0级数据（求和后），对于HWLS经向风速即用RayNLS、纬向风速即用RayWLS ，对于HWSM经向风速即用RayNHS、纬向风速即用RayWHS
            'N_sum2': conf['channel2'],                 # 通道2的L0级数据（求和后），对于HWLS经向风速即用RayNLR、纬向风速即用RayWLR ，对于HWSM经向风速即用RayNHR、纬向风速即用RayWHR
            'Altitude_final': conf['Height'],         # L2级探测文件数据高度，如[20,21,……,80]
            'dead_time': getValueByParameterName(parameter_list, "探测器死区时间"),                   # 探测器死区时间
            'uncertain_dead_time': getValueByParameterName(parameter_list, "探测器死区时间波动度"),   # 探测器死区时间波动度
            'dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声"),                    # 探测器暗噪声
            'uncertain_dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声波动度"),    # 探测器暗噪声波动度
            'quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率"),          # 探测器量子效率
            'uncertain_quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率波动度"), # 探测器量子效率波动度
            'pulse_repeat': getValueByParameterName(parameter_list, "激光器重频"),                    # 激光器重频
            'pulse_time': getValueByParameterName(parameter_list, "L2级积分时间"),                    # L2级积分时间
            'wavelength': getValueByParameterName(parameter_list, "激光器中心波长"),                  # 激光器中心波长
            'zenith_angle': getValueByParameterName(parameter_list, "望远镜天顶角"),                  # 望远镜天顶角
        }
        print("MCM计算参数（HWLS、HWSM）：")
        print(param)

        mcm_result_vec = inversion_Rayleigh_wind(param['n_MCM'],param['N_sum1'],param['N_sum2'],
                                param['Altitude_final'],param['dead_time'],param['uncertain_dead_time'],
                                param['dark_noise'],param['uncertain_dark_noise'],param['quantum_efficiency'],
                                param['uncertain_quantum_efficiency'],param['pulse_repeat'],
                                param['pulse_time'],param['wavelength'],param['zenith_angle'])

    # 瑞利测温（适用于TUSM）
    elif data_type == "TUSM":
        param = {
            'n_MCM': conf['McmParameterExpTimes'],      # MCM试验次数
            'S_time_sum': conf['RayVHS'],                 # 探测文件各高度L0级数据（求和后）
            'Altitude_final': conf['Height'],             # L2级探测文件数据高度，如[20,21,……,80]
            'dead_time': getValueByParameterName(parameter_list, "探测器死区时间"),                   # 探测器死区时间
            'uncertain_dead_time': getValueByParameterName(parameter_list, "探测器死区时间波动度"),   # 探测器死区时间波动度
            'dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声"),                    # 探测器暗噪声
            'uncertain_dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声波动度"),    # 探测器暗噪声波动度
            'quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率"),          # 探测器量子效率
            'uncertain_quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率波动度"), # 探测器量子效率波动度
            'pulse_repeat': getValueByParameterName(parameter_list, "激光器重频"),                   # 激光器重频
            'pulse_time': getValueByParameterName(parameter_list, "L2级积分时间"),                   # L2级积分时间
            'altitudeSysten_MCM': getValueByParameterName(parameter_list, "系统所在高度"),           # 系统所在高度
            'altitude_inf': getValueByParameterName(parameter_list, "参考密度所在高度"),             # 参考密度所在高度
            'density_inf': getValueByParameterName(parameter_list, "参考密度"),                      # 参考密度
            'uncertain_densityRef': getValueByParameterName(parameter_list, "参考密度波动度"),       # 参考密度波动度
            'altitude_invStart': getValueByParameterName(parameter_list, "密度积分起始高度"),        # 密度积分起始高度
            'Temperature_inf': getValueByParameterName(parameter_list, "密度积分起始温度"),          # 密度积分起始温度
            'uncertain_temperatureRef': getValueByParameterName(parameter_list, "参考温度波动度"),   # 参考温度波动度
        }
        print("MCM计算参数（TUSM）：")
        print(param)

        mcm_result_vec = inversion_Rayleigh_temperature(param['n_MCM'],param['S_time_sum'],
                                param['Altitude_final'],param['dead_time'],param['uncertain_dead_time'],
                                param['dark_noise'],param['uncertain_dark_noise'],param['quantum_efficiency'],
                                param['uncertain_quantum_efficiency'],param['pulse_repeat'],param['pulse_time'],
                                param['altitudeSysten_MCM'],param['altitude_inf'],param['density_inf'],
                                param['uncertain_densityRef'],param['altitude_invStart'],
                                param['Temperature_inf'],param['uncertain_temperatureRef'])

    # 荧光测风（适用于HWSL）
    elif data_type == "HWSL":
        param = {
            'n_MCM': conf['McmParameterExpTimes'],      # MCM试验次数
            'N_sum1': conf['channel1'],                     # 通道1的L0级数据（求和后），经向风速即用NaNF0、纬向风速即用NaWF0
            'N_sum2': conf['channel2'],                     # 通道2的L0级数据（求和后），经向风速即用NaNF1、纬向风速即用NaWF1
            'N_sum3': conf['channel3'],                     # 通道2的L0级数据（求和后），经向风速即用NaNF2、纬向风速即用NaWF2
            'Altitude_final': conf['Height'],             # L2级探测文件数据高度，如[20,21,……,80]
            'dead_time': getValueByParameterName(parameter_list, "探测器死区时间"),          # 探测器死区时间
            'uncertain_dead_time': getValueByParameterName(parameter_list, "探测器死区时间波动度"), # 探测器死区时间波动度
            'dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声"),           # 探测器暗噪声
            'uncertain_dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声波动度"),  # 探测器暗噪声波动度
            'quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率"), # 探测器量子效率
            'uncertain_quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率波动度"), # 探测器量子效率波动度
            'pulse_repeat': getValueByParameterName(parameter_list, "激光器重频"),          # 激光器重频
            'pulse_time': getValueByParameterName(parameter_list, "L2级积分时间"),          # L2级积分时间
            'zenith_angle': getValueByParameterName(parameter_list, "望远镜天顶角"),        # 望远镜天顶角
        }
        print("MCM计算参数（HWSL）：")
        print(param)

        mcm_result_vec = inversion_ResW(param['n_MCM'],param['N_sum1'],param['N_sum2'],param['N_sum3'],
                                param['Altitude_final'],param['dead_time'],param['uncertain_dead_time'],
                                param['dark_noise'],param['uncertain_dark_noise'],param['quantum_efficiency'],
                                param['uncertain_quantum_efficiency'],param['pulse_repeat'],
                                param['pulse_time'],param['zenith_angle'])

    # 荧光测温（适用于TMSL）
    elif data_type == "TMSL":
        param = {
            'n_MCM': conf['McmParameterExpTimes'],  # MCM试验次数
            'N_sum1': conf['NaVF0'],                     # 通道1的L0级数据（求和后），NaNF0
            'N_sum2': conf['NaVF1'],                     # 通道2的L0级数据（求和后），NaNF1
            'N_sum3': conf['NaVF2'],                     # 通道2的L0级数据（求和后），NaNF2
            'Altitude_final': conf['Height'],               # L2级探测文件数据高度，如[20,21,……,80]
            'dead_time': getValueByParameterName(parameter_list, "探测器死区时间"),                  # 探测器死区时间
            'uncertain_dead_time': getValueByParameterName(parameter_list, "探测器死区时间波动度"),  # 探测器死区时间波动度
            'dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声"),                   # 探测器暗噪声
            'uncertain_dark_noise': getValueByParameterName(parameter_list, "探测器暗噪声波动度"),   # 探测器暗噪声波动度
            'quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率"),         # 探测器量子效率
            'uncertain_quantum_efficiency': getValueByParameterName(parameter_list, "探测器量子效率波动度"),  # 探测器量子效率波动度
            'pulse_repeat': getValueByParameterName(parameter_list, "激光器重频"),          # 激光器重频
            'pulse_time': getValueByParameterName(parameter_list, "L2级积分时间"),          # L2级积分时间
            'zenith_angle': getValueByParameterName(parameter_list, "望远镜天顶角"),        # 望远镜天顶角
        }
        print("MCM计算参数（TMSL）：")
        print(param)

        mcm_result_vec = inversion_ResT(param['n_MCM'],param['N_sum1'],param['N_sum2'],param['N_sum3'],
                            param['Altitude_final'],param['dead_time'],param['uncertain_dead_time'],
                            param['dark_noise'],param['uncertain_dark_noise'],param['quantum_efficiency'],
                            param['uncertain_quantum_efficiency'],param['pulse_repeat'],
                            param['pulse_time'],param['zenith_angle'])

    else:
        print("探测体制不支持: %s" % data_type)
        return -1

    return mcm_result_vec


###########################
# MCM算法：不同探测体制对应的设备参数文件
systemTypeFileForMcm = {
    "T": {"system_type": "RayT", "file_name": "dataInput_ Lidar02_RayT.csv", "description": "瑞利测温"},
    "P": {"system_type": "xxx", "file_name": "xxx", "description": "xxx测压"},                            # TODO：压力没有计算方法 ？？
    "W1": {"system_type": "RayW", "file_name": "dataInput_ Lidar02_RayW.csv", "description": "瑞利测风"},
    "W2": {"system_type": "RayW", "file_name": "dataInput_ Lidar02_RayW.csv", "description": "瑞利测风"},
    "D": {"system_type": "RayT", "file_name": "dataInput_ Lidar02_RayT.csv", "description": "瑞利测密"},
    "N": {"system_type": "RayT", "file_name": "dataInput_ Lidar02_RayT.csv", "description": "钠原子数密度"},
    "F": {"system_type": "RayT", "file_name": "dataInput_ Lidar02_RayT.csv", "description": "铁原子密度"},

    #以下为老的模拟数据探测体制，后续不再使用
    "DUSM": {"system_type": "RayT", "file_name": "dataInput_ Lidar02_RayT.csv", "description": "瑞利测密"},
    "TUSM": {"system_type": "RayT", "file_name": "dataInput_ Lidar02_RayT.csv", "description": "瑞利测温"},
    "TSVP": {"system_type": "RamT", "file_name": "dataInput_ Lidar02_RamT.csv", "description": "拉曼测温"},
    "HWLS": {"system_type": "RayW", "file_name": "dataInput_ Lidar02_RayW.csv", "description": "瑞利测风"},
    "HWSM": {"system_type": "RayW", "file_name": "dataInput_ Lidar02_RayW.csv", "description": "瑞利测风"},
    "TMSL": {"system_type": "ResW", "file_name": "dataInput_ Lidar02_ResW.csv", "description": "荧光测温测风"},
    "HWSL": {"system_type": "ResW", "file_name": "dataInput_ Lidar02_ResW.csv", "description": "荧光测温测风"}
}

# MCM算法：不同设备参数类型对应的参数值（此变量仅用于测试）
# 已修改成从数据库实时获取
deviceLidar02SystemTypeInfoForMcm_bak = {
    # 拉曼测温：SystemTypeCode=0
    "RamT": {"file_name": "dataInput_Lidar02_RamT.csv",
             "parameter": [{"Parameter": "探测器量子效率", "Value": "10", "Unit": "%"},
                           {"Parameter": "探测器量子效率波动度", "Value": "0.1", "Unit": "%"},
                           {"Parameter": "探测器死区时间波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "探测器暗噪声波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "系统所在高度", "Value": "0.5", "Unit": "%"},
                           {"Parameter": "L0级高度分辨率", "Value": "0.02", "Unit": "km"},
                           {"Parameter": "激光器重频", "Value": "10", "Unit": "Hz"},
                           {"Parameter": "探测器暗噪声", "Value": "300", "Unit": "1"},
                           {"Parameter": "探测器死区时间", "Value": "8.00E-10", "Unit": "s"},
                           {"Parameter": "L2级积分时间", "Value": "1200", "Unit": "s"},
                           {"Parameter": "拟合系数", "Value": "[3, 2, 1]", "Unit": "1"},
                           {"Parameter": "拟合系数a", "Value": "2", "Unit": "1"},
                           {"Parameter": "拟合系数b", "Value": "3", "Unit": "1"},
                           {"Parameter": "拟合系数c", "Value": "1", "Unit": "1"}]},
    # 瑞利测温：SystemTypeCode=1
    "RayT": {"file_name": "dataInput_Lidar02_RayT.csv",
             "parameter": [{"Parameter": "探测器量子效率波动度", "Value": "0.1", "Unit": "%"},
                           {"Parameter": "探测器量子效率", "Value": "10", "Unit": "%"},
                           {"Parameter": "探测器死区时间波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "探测器暗噪声波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "系统所在高度", "Value": "0.5", "Unit": "%"},
                           {"Parameter": "参考密度", "Value": "5", "Unit": "%"},
                           {"Parameter": "参考密度波动度", "Value": "1", "Unit": "%"},
                           {"Parameter": "参考温度波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "探测器暗噪声", "Value": "300", "Unit": "1"},
                           {"Parameter": "大气分子质量波动", "Value": "1", "Unit": "%"},
                           {"Parameter": "重力加速度波动", "Value": "1", "Unit": "%"},
                           {"Parameter": "L0级高度分辨率", "Value": "0.02", "Unit": "km"},
                           {"Parameter": "激光器重频", "Value": "10", "Unit": "Hz"},
                           {"Parameter": "探测器死区时间", "Value": "8.00E-10", "Unit": "s"},
                           {"Parameter": "L2级积分时间", "Value": "1200", "Unit": "s"},
                           {"Parameter": "参考密度所在高度", "Value": "40", "Unit": "km"},
                           {"Parameter": "参考高度处的大气密度", "Value": "0.004052", "Unit": "kg/m^3"},
                           {"Parameter": "密度积分起始高度", "Value": "90", "Unit": "km"},
                           {"Parameter": "密度积分起始温度", "Value": "186.292", "Unit": "K"}]},
    # 瑞利测风：SystemTypeCode=2
    "RayW": {"file_name": "dataInput_Lidar02_RayW.csv",
             "parameter": [{"Parameter": "探测器量子效率波动度", "Value": "0.1", "Unit": "%"},
                           {"Parameter": "探测器量子效率", "Value": "10", "Unit": "%"},
                           {"Parameter": "探测器死区时间波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "系统所在高度", "Value": "0.5", "Unit": "%"},
                           {"Parameter": "参考温度波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "探测器暗噪声波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "L0级高度分辨率", "Value": "0.02", "Unit": "km"},
                           {"Parameter": "L2级积分时间", "Value": "1200", "Unit": "s"},
                           {"Parameter": "激光器中心波长", "Value": "5.32E+02", "Unit": "nm"},
                           {"Parameter": "激光器重频", "Value": "10", "Unit": "Hz"},
                           {"Parameter": "探测器暗噪声", "Value": "300", "Unit": "1"},
                           {"Parameter": "望远镜天顶角", "Value": "15", "Unit": "1"},
                           {"Parameter": "探测器死区时间", "Value": "8.00E-10", "Unit": "s"}]},
    # 共振荧光测温测风：SystemTypeCode=3
    "ResW": {"file_name": "dataInput_Lidar02_ResW.csv",
             "parameter": [{"Parameter": "探测器量子效率波动度", "Value": "0.1", "Unit": "%"},
                           {"Parameter": "探测器量子效率", "Value": "10", "Unit": "%"},
                           {"Parameter": "探测器死区时间波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "系统所在高度", "Value": "0.5", "Unit": "%"},
                           {"Parameter": "参考温度波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "探测器暗噪声波动度", "Value": "5", "Unit": "%"},
                           {"Parameter": "L0级高度分辨率", "Value": "0.02", "Unit": "km"},
                           {"Parameter": "激光器重频", "Value": "10", "Unit": "Hz"},
                           {"Parameter": "探测器暗噪声", "Value": "300", "Unit": "1"},
                           {"Parameter": "望远镜天顶角", "Value": "15", "Unit": "1"},
                           {"Parameter": "探测器死区时间", "Value": "8.00E-10", "Unit": "s"},
                           {"Parameter": "L2级积分时间", "Value": "1200", "Unit": "s"}]},
}


def getDeviceParameterBySystemTypeFromBD():
    """
    从数据库生成变量：deviceLidar02SystemTypeInfoForMcm（取代上面的内容）
    """
    deviceLidar02SystemTypeInfoForMcm = {
        # 拉曼测温：SystemTypeCode=0
        "RamT": {"file_name": "dataInput_Lidar02_RamT.csv",
                 "SystemTypeCode": 0,
                 "parameter": []},
        # 瑞利测温：SystemTypeCode=1
        "RayT": {"file_name": "dataInput_Lidar02_RayT.csv",
                 "SystemTypeCode": 1,
                 "parameter": []},
        # 瑞利测风：SystemTypeCode=2
        "RayW": {"file_name": "dataInput_Lidar02_RayW.csv",
                 "SystemTypeCode": 2,
                 "parameter": []},
        # 共振荧光测温测风：SystemTypeCode=3
        "ResW": {"file_name": "dataInput_Lidar02_ResW.csv",
                 "SystemTypeCode": 3,
                 "parameter": []},
    }
    db_table = "bt_device_system_type"

    for key in deviceLidar02SystemTypeInfoForMcm.keys():
        print(key)
        system_type_code = deviceLidar02SystemTypeInfoForMcm[key]['SystemTypeCode']
        query_conditon = 'SystemType = %d' % system_type_code
        print("query_conditon: %s" % query_conditon)
        ret = select_db(db_table, query_conditon)
        print(ret, len(ret))
        if len(ret) > 0:
            ret = dbQueryResult2JsonObj(ret, db_table)
            for item in ret:
                param_obj = {"Parameter": item['Parameter'],
                             "Value": item['Value'],
                             "Unit": item['Unit']}
                deviceLidar02SystemTypeInfoForMcm[key]['parameter'].append(param_obj)
        else:
            deviceLidar02SystemTypeInfoForMcm[key] = []

    return deviceLidar02SystemTypeInfoForMcm


def getDeviceParameterBySystemTypeForMcmAlgorithm(deviceLidar02SystemTypeInfoForMcm, data_type):
    """
    该函数为临时策略，正常流程为从数据库表读入设备体制信息
    :return:
    """
    if data_type not in systemTypeFileForMcm.keys():
        print("不支持的探测体制：%s" % data_type)
        return ""

    device_param_type = systemTypeFileForMcm[data_type]
    device_paramter_type_info = deviceLidar02SystemTypeInfoForMcm[device_param_type['system_type']]

    return device_paramter_type_info['parameter']


def getValueByParameterName(parameter_json_list, parameter_name, flag=1):
    """
     从函数getDeviceParameterBySystemTypeForMcmAlgorithm结果中查找指定参数名称的值
     flag=1 返回浮点型；flag=0 返回字符串
    """
    #print(parameter_json_list)
    #print(parameter_name)
    for i in range(len(parameter_json_list)):
        item = parameter_json_list[i]
        #print(item)
        if item['Parameter'] == parameter_name:
            if flag:
                return float(item['Value'])
            else:
                return item['Value']
    return ""


def getListByParameterName(parameter_json_list, parameter_name):
    for i in range(len(parameter_json_list)):
        item = parameter_json_list[i]

        if item['Parameter'] == parameter_name:
            value = item['Value']
            param_arr = value[1:len(value)-1].split(',')
            ret_list = []
            for item in param_arr:
                ret_list.append(str_to_float(item))

            return ret_list
    return []
